Agent bridge contract: the page's calls to the app are checked from both sides; Add account reaches the page host

Merged
TE
1d ago
+751-0

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.json lists:
    • every cmux.agent.* op with its bridge method;
    • the param keys AgentPaneReplyRequest and AgentPaneEnvironmentRequest accept;
    • 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.
  • agentBridgeContract.test.ts (webviews, bun): the test parses webviews/src/agent-session with the TypeScript compiler. It finds every method literal sent through postNative or a wrapper of it (wrappers that try cmuxAcpmuxActions first may also name the page's direct actions), along with its literal param keys and every messageHandlers name. 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.methods or 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 from AgentPageOps. On the shared page host it failed with cmux.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.ts passed 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
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.json lists every op, the param keys, legacy-only and unserved methods, and the WebKit handler names.
  • agentBridgeContract.test.ts (webviews) parses webviews/src/agent-session with 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's AgentPageOps, its param tables, or the old bridge's decoder drift from the contract.
  • account.add was sent by the page and decoded by the old bridge but missing from AgentPageOps; it is now an op, so Add account reaches the app on the shared page host.