Skip to content

[codex] feat: add Runtime Host capability RPC - #513

Merged
madawei2699 merged 5 commits into
cf-sfufrom
codex/runtime-capability-512
Sep 29, 2026
Merged

madawei2699 merged 5 commits into
cf-sfufrom
codex/runtime-capability-512

Conversation

@madawei2699

@madawei2699 madawei2699 commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Implements the two Runtime capability halves tracked by Free4Chat Core #512: a Runtime-local semantic controller and a bounded, deterministic Human-to-Room-to-Runtime capability RPC.

The Runtime owns a fixed localhost fixture adapter and its private endpoint configuration. Room/Core carries only bounded semantic descriptors and request/results over the existing private Resident WebSocket. Human requests do not enter Room chat, Task ingestion, Harness scheduling, or persistent Room state.

Wire contract

  • Runtime Host projections may carry one capability descriptor with a bounded ID, title/version, observe flag, and up to four typed actions.
  • Human control and request frames use runtime-capability-control and runtime-capability-request.
  • Core routes the private request to a verified, connected Agent on the exact Host, then accepts a correlated result only from that same participant and resident connection nonce.
  • Results return only to the requesting Human socket. Late, duplicate, malformed, or mismatched results are rejected or ignored with a closed error set.
  • Both directions use the existing resident event stream. No additional service, public event, or reverse tunnel is introduced.

Authorization and bounds

  • Only the Host's associated Human owner can enable or disable capability control. The explicit grant is independent of speech provider authority.
  • Requests require a current authenticated Human connection, the owner grant, an advertised capability/action, and a live verified resident for that exact Host.
  • Room bounds include one capability per Host, four actions, 1 KiB descriptors, 8 KiB arguments, 16 KiB results, four in-flight requests, a 10 second Core timeout, and at most eight capability-bearing Room Hosts.
  • Runtime-local limits remain independently enforced: one bounded fixture capability, 1 KiB arguments, 4 KiB results, a 2 KiB descriptor, a 256 byte loopback endpoint, and a 1.5 second controller timeout.
  • Requests/results are transient in Room memory. Only bounded correlation and authority metadata is attached to the resident socket. Disconnect, replacement, revoke, capability removal, timeout, and Room teardown clear pending work.
  • The local endpoint remains in a mode-0600 Runtime config and never enters descriptors, results, errors, status, logs, or Room projection. The fixture adapter uses fixed routes.

Human and Agent paths

The paired Human owner can review the descriptor, opt in, observe, and invoke typed actions in the Room UI. Production daemon residents now receive a stable daemon-owned capability delegate; each descriptor/request resolves the daemon's current controller, including after live capability configure. Live configure refreshes each online resident's Runtime Host projection so Room discovery updates without reconnecting.

A fresh Harness bootstrap teaches generic local discovery through $FREE4CHAT_AGENT_BIN capability list --json, followed by the bounded describe/observe/invoke commands using IDs and schemas returned by the daemon. The command returns validated semantic projections only. It does not include endpoint/configuration values. The prompt leaves local authorization and approvals to Harness/operator policy; Room input itself grants no local authority.

Runtime-local fixture

The daemon-owned controller supports the configured local fixture through fixed GET /state and POST /actions/set-led routes. CLI and Human RPC calls reach the same daemon-owned controller. No arbitrary proxy/path API is exposed.

Follow-up regressions for review comment 5890825840

  • TestDaemonCapabilityConfigureRefreshesExistingResidentAndRoutesThroughCurrentController starts a real daemon resident, configures after its private event socket is online, verifies the Host descriptor refresh, replaces the configured controller, then routes a Resident request and proves the current daemon controller answers.
  • TestCapabilityListDiscoversCurrentBoundedDescriptorThroughDaemon covers generic ID/schema discovery with no Human-supplied ID and checks endpoint/config values are absent.
  • TestBootstrapDiscoversGenericRuntimeLocalCapabilityWithoutRoomAuthority pins the generic command path, source-search prohibition, and local approval authority rules without embedding fixture/action semantics.

Validation

  • Frontend CI: Prettier, ESLint, TypeScript type check, docs asset check, and full yarn test — 125 files, 1,412 tests passed.
  • Go CI: gofmt check, go vet ./..., go test ./... -count=1 -timeout 300s, go test -race -count=1 ./internal/voice, and go build ./cmd/free4chat-agent.
  • Distribution checks: cleanup contract, installer tests (19 passed), and fresh bootstrap contract.
  • Focused daemon, CLI, and Harness regressions passed.

This remains a draft PR based on cf-sfu; no Runtime release work is included.

@madawei2699 madawei2699 changed the title [codex] feat(agent): add local Runtime capability controller [codex] feat: add Runtime Host capability RPC Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

Final review found two focused E2E gaps before #513 can satisfy Core #512. The architecture and bounded Room RPC look sound; CI is green. Please fix these in this same PR rather than opening another implementation PR.

1. Production daemon does not wire the local controller into resident Runtime

The daemon owns d.localCapability, and the CLI/IPC calls use it, but prepareRuntime() currently constructs runtime.Options{...} without setting CapabilityHandler.

That means the real resident Runtime gets a nil handler:

  • CurrentHostProjection() projects no capability;
  • the Room cannot discover/enable it;
  • a routed resident capability request fails closed instead of reaching the daemon-owned controller.

Please add a real daemon→resident integration regression, not only isolated Runtime tests.

Also account for capability configure replacing d.localCapability while residents may already be running. A one-time pointer copy at join would leave existing residents stale. Use the smallest stable daemon-owned delegation/refresh mechanism so:

  • every same-daemon resident reaches the current controller;
  • configuring the fixture after a resident is already online causes the Host projection to refresh, or explicitly remove hot configuration from the contract and make that ordering truthful. Prefer hot refresh because the CLI currently presents configure as a live daemon operation.

Acceptance regression should exercise:
daemon configure → existing resident Host projection gains descriptor → Room-side resident request → same daemon controller.

2. Agent semantic path is not yet discoverable

#512 requires the resident Agent to discover the bounded capability description and invoke the same semantic controller. The local CLI commands exist, but the Harness bootstrap currently documents collab/attach/surface/context/room-app only; agent/internal/harness/prompt.go has no Runtime capability affordance, and the turn context does not surface the local descriptor.

Do not hard-code LED behavior into the prompt. Add the smallest generic Agent-facing discovery seam, for example:

  • project the Runtime-local bounded descriptor(s) into the Harness bootstrap and document the exact $FREE4CHAT_AGENT_BIN capability describe/observe/invoke commands; or
  • add an equally bounded local list/describe discovery command and teach that generic command in the bootstrap.

Required properties:

  • endpoint/config/credential never enters the prompt;
  • Room input remains untrusted and does not itself grant local authority;
  • Agent observe/invoke hits the same daemon-owned controller as Human RPC;
  • add a prompt/runtime regression proving a fresh Harness can discover the capability ID/schema without source grep or Human-supplied ID.

Once these two gaps are fixed, rerun the existing full TS/Go matrices plus the new daemon integration and Harness-discovery regressions. No Runtime release work yet.

Copy link
Copy Markdown
Collaborator Author

Final code review after commit 5c3f782f: implementation PASS; keep Draft pending one isolated E2E dogfood gate.

The two prior blockers are resolved:

  • Production residents receive a stable daemon-owned capability delegate, not a stale controller pointer. capability configure replaces the daemon controller and refreshes online Runtime Host projections.
  • Fresh Harness bootstrap now exposes generic capability list --json → describe → observe/invoke discovery without fixture-specific semantics, endpoint/config values, or weakening the existing local authority rules.

The new daemon regression exercises live configure after the resident socket is online, projection refresh, controller replacement, and Resident RPC dispatch through the current daemon controller. The Harness regression pins generic discovery and the no-source-search/no-Room-authority contract.

GitHub workflows for head 5c3f782f5f294d01f6666265d20fa58b4d24273b are green: Agent Runtime, Room App Host Compatibility, Agent Release checks, PR Autofix, and CI tests. In CI / Deploy, however, Build & Deploy is skipped for this PR, so there is no deployed branch evidence yet.

Remaining gate before merge:

  1. isolated test Worker built from this exact PR head;
  2. branch Runtime binary built from the same head;
  3. localhost fixture;
  4. live configure while resident is already online;
  5. authorized Human observe/invoke without waking Harness;
  6. unrelated Human denied;
  7. addressed Agent discovers capability via generic CLI and invokes the same local controller;
  8. Runtime departure removes/unavailable capability;
  9. verify endpoint/secret never appears in Room/prompt/UI/log evidence.

No more architecture work is requested unless this dogfood exposes a concrete bug.

@madawei2699

Copy link
Copy Markdown
Collaborator Author

Final isolated E2E dogfood is BLOCKED before Phase 1; no test Worker was deployed and no code was changed.

  • Confirmed checkout and PR head: 5c3f782f5f294d01f6666265d20fa58b4d24273b.
  • Confirmed Extension Lab main contains research/local-capability-fixture/ (main SHA 242527775d5236cac939bd4a8860a451512ed18c).
  • The Core Wrangler config binds the production ROOMS_KV namespace and production control-plane/self service. There is no repository test Worker deploy runbook or isolated Worker/KV configuration. The account's current KV list has only the configured Core namespace; I found no dedicated test namespace.
  • Wrangler auth is present, but its reported permissions do not include Realtime Calls write access. A dedicated SFU app and credentials are therefore unavailable. The browser-to-Room flow cannot be exercised safely against an isolated Worker without those resources.
  • Exact-head CI is green; its Build & Deploy job was skipped. No production deployment occurred.

I stopped before starting a resident, fixture, or browser session. This means every runtime E2E acceptance item remains unverified, not passed. To continue, provide a dedicated test Realtime SFU app (App ID/secret through a secure secret channel) and isolated Worker/KV resources, or refresh the Wrangler authorization with Calls write access and confirm that I should provision disposable test resources. Once available, I can resume on this exact PR head. PR remains draft and unmerged.

@madawei2699

Copy link
Copy Markdown
Collaborator Author

Correction to my earlier blocker report: I had not checked the repository dogfood skill before reporting that isolated deployment and Turnstile were unavailable. That was my mistake. The skill supports a uniquely named temporary workers.dev Worker, isolated KV, and a Turnstile bypass confined to that temporary config. I have deployed that isolated Worker and am continuing the real browser/Runtime flow; production Worker routes and config remain untouched. I will replace this interim correction with the verified E2E result after cleanup.

@madawei2699

Copy link
Copy Markdown
Collaborator Author

Final #512 E2E and regression update

Commit: 710f80efd831801a123f1c10dddf574ef74e4a42 (fix: forward Runtime capability projections through MCP).

The final E2E exposed one remaining Room projection gap: the MCP Zod schema stripped runtimeHost.capabilities on both join_room and update_runtime_host. The schema now accepts only bounded, secret-free capability descriptors, and the regression covers both paths. The daemon live-config refresh/controller-routing regression and the generic Harness list → describe → observe → invoke discovery regression also pass.

Deployed dogfood used an isolated temporary workers.dev Worker with Turnstile disabled only in that temporary configuration. In two independent browser contexts, an already-online Runtime gained the capability projection after daemon configure; Human control and a generic Agent discovery/invocation both reached the same daemon-owned controller. The Agent discovered the bounded descriptor through Runtime commands, observed and invoked it under explicit Harness/operator approvals, and did not receive an endpoint, config, credential, or supplied capability ID in its prompt. The second Human context had no local controls. The temporary Worker and its isolated KV namespace have been deleted; the temporary URL returns 404, and production free4chat-realtime remains on version 16eea9ed-3324-448b-aab5-c411b7574316.

Local verification after temporary-resource cleanup:

  • Web CI: Prettier, ESLint, TypeScript, docs checks, docs contract tests, and full Vitest (125 files / 1,413 tests) passed.
  • Go CI: gofmt, vet, full tests, targeted voice race test, build, cleanup contract, installer tests, and bootstrap contract passed.
  • Focused daemon live-config/controller-routing and Harness generic discovery tests passed.

The existing PR remains Draft and open. GitHub Actions for this pushed commit were still pending when this update was posted. No merge, release, or production deployment was performed.

@madawei2699

Copy link
Copy Markdown
Collaborator Author

Follow-up: GitHub Actions have now completed successfully for this update. App Tests, Lint & Type Check, Room App host matrix, Go test/vet/build, Distribution / cleanup contract, Format & lint autofix, and all four release build targets passed. Build & Deploy and Publish GitHub Release were skipped by their workflow conditions; the PR remains Draft and no deployment or release occurred.

Copy link
Copy Markdown
Collaborator Author

Final gate: PASS / ready to merge at exact head 710f80efd831801a123f1c10dddf574ef74e4a42.

Why this is sufficient:

  • isolated dogfood exercised live configure on an already-online resident;
  • Human deterministic Room RPC and Agent generic list→describe→observe→invoke both reached the same daemon-owned controller;
  • a second independent Human had no local control surface;
  • prompt/runtime evidence did not expose endpoint/config/credential/capability ID;
  • the dogfood found and fixed a real MCP admission bug: runtimeHost.capabilities was stripped by the Zod schema on join_room / update_runtime_host;
  • both paths now share a bounded, secret-filtering schema with regression coverage;
  • local + GitHub App/Go/host-matrix/distribution/release-build checks are green;
  • lifecycle regressions cover unauthorized caller, timeout, revoke, resident disconnect, capability removal/Room expiry cleanup.

Temporary Worker/KV cleanup is complete and production was not changed during dogfood.

#512 should remain open after source merge until the merged Core is deployed and a released Runtime binary containing this protocol is accepted.

@madawei2699
madawei2699 marked this pull request as ready for review September 29, 2026 16:27
@madawei2699
madawei2699 merged commit 2837fa9 into cf-sfu Sep 29, 2026
15 checks passed
@madawei2699
madawei2699 deleted the codex/runtime-capability-512 branch September 29, 2026 16:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 710f80efd8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +108 to +110
(candidate.capabilityControlHumanParticipantId === undefined ||
candidate.capabilityControlHumanParticipantId ===
candidate.humanParticipantId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rebind capability control during provider reattachment

When capability control is enabled and a browser reload reattaches the provider under a new Human participant ID, both reattachRuntimeHostProvider and completeDeferredRuntimeHostProviderReattach retain the old capabilityControlHumanParticipantId while replacing humanParticipantId. This new validation consequently rejects the association during the next activeRoom() normalization, dropping the entire private provider binding and potentially invalidating an active Live Transcript as well as capability control. Clear the grant or transfer it to the replacement participant during both reattachment paths.

AGENTS.md reference: AGENTS.md:L108-L110

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants