Agent bridge contract: the page's calls to the app are checked from both sides; Add account reaches the page host
Leo, 2026-11-05: "You guys keep running into these. These are weird issues that I feel like you should be able to just fix beforehand and check on beforehand."
The agent pane page names its calls to the app by hand (postNative("chat.tail", …) and a dozen wrappers), and the app lists the ones it serves by hand (AgentPageOps, the old bridge's decoder, per-method param tables). Nothing compared the two sides. This adds one contract that both sides are checked against, the way #36260 does for the CLI and the app.
What changes
schemas/agent-bridge/contract.jsonlists:- every
cmux.agent.*op with its bridge method; - the param keys
AgentPaneReplyRequestandAgentPaneEnvironmentRequestaccept; - the methods only the old WebKit bridge decodes (
contextMenu.show,shell.complete); - the methods the page sends that no app serves yet (
models.catalog; the page keeps its bundled catalog); - the WebKit handler names the webviews post to, and which ones belong to the classic app or prototypes.
- every
agentBridgeContract.test.ts(webviews, bun): the test parseswebviews/src/agent-sessionwith the TypeScript compiler. It finds every method literal sent throughpostNativeor a wrapper of it (wrappers that trycmuxAcpmuxActionsfirst may also name the page's direct actions), along with its literal param keys and everymessageHandlersname. It fails on any that the contract lacks, and on a listed app handler that no Swift source registers. A self-test plants a drifted method, a stray key and an unlisted handler.AgentBridgeContractTests(Swift): it fails on any of these:AgentPageOps.methodsor the param tables differ from the contract (the failure prints the lines to paste);- the old bridge's decoder handles a method that is neither an op nor listed as legacy-only;
- an "unserved" method becomes served.
- Drift fixed:
account.add(the model menu's Add account) was sent by the page and decoded by the old bridge, but it was missing fromAgentPageOps. On the shared page host it failed withcmux.protocol.unknown_op. It is an op now, and the gesture gate still applies.
Changelog
Fixed: Add account in the agent pane's model menu reaches the app on the shared page host.
Verification
bun test src/agent-session/acpmux/agentBridgeContract.test.tspassed on the fleet: 3 tests.- The Swift suite runs in this PR's
cmux-next swift test.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds one shared contract the agent pane page and the app are both checked against, so drifted bridge methods fail tests instead of at runtime. Also fixes Add account in the model menu failing with cmux.protocol.unknown_op on the shared page host.
schemas/agent-bridge/contract.jsonlists every op, the param keys, legacy-only and unserved methods, and the WebKit handler names.agentBridgeContract.test.ts(webviews) parseswebviews/src/agent-sessionwith the TypeScript compiler and fails on any method, param key, or handler name the contract lacks, including methods sent through a callback sender. Only callbacks that default to a known sender (send = postNative) or get one passed at a call site count; unrelated(method: string)callbacks are not scanned.AgentBridgeContractTests(Swift) fails when the app'sAgentPageOps, its param tables, or the old bridge's decoder drift from the contract.account.addwas sent by the page and decoded by the old bridge but missing fromAgentPageOps; it is now an op, so Add account reaches the app on the shared page host.