cmux NEXT: the pane may answer a session offer (offer_respond, scoped)
Summary
The agent pane page sends _acpmux/offer_respond since #20337: Dismiss on an offer chip sends {sessionId, offerId, outcome: "dismissed"}. Start sends {sessionId, offerId, outcome: "accepted", childSessionId} right after the page's own session/new for the child, then a session/prompt to the child. The host allowlist did not have the method, so the host refused both, and cmux-agent-pane-policy/tests/typescript.rs::the_allowlist_is_what_the_page_sends was red on base.
This PR lets the method through, scoped to the pane's own sessions, in Swift (AcpmuxPaneMethods, +Params, AgentPaneTransport+Check) and in its Rust mirror (policy.json, data.rs, params.rs):
- It is in
requestsandsessionScoped/session_scoped.sessionId, the chat that holds the offer, must be in the pane's scope. - New per-method optional scoped-params map
optionalScopedSessionParams/optional_scoped_session_params, with"_acpmux/offer_respond": ["childSessionId"]. An absent param passes. A present one must be a string the pane holds;nullor a non-string is refused. This is needed because Dismiss names no child, and the existingscopedSessionParamsrequires its params to be present. - Params allowlist:
sessionId,offerId,outcome,childSessionId. No_meta, because no gesture ticket is involved. - No gesture rule. Dismiss declines. Start only links the page's own new chat, and the work it begins is its
session/prompt, which already needs a gesture.
Security
- Local command/content execution: none. The frame only asks acpmux to record the answer to an offer and, on accept, to link a child chat the pane already started. It carries no command, path, prompt text or environment, and unknown params are refused. The agent turn that follows is a separate
session/prompt, which keeps its gesture rule. - Access to unowned objects: both session ids are pane-scoped.
sessionIdgoes throughsessionScoped.childSessionId, when present, must also be a session the pane started or shows, so the page cannot link a session it does not hold.offerIdis checked by the daemon against that chat (#20305). Agents cannot call the method: the daemon accepts it only from the person (#20305's person gate). - The optional-param rule, and why
_acpmux/sidestays strict: the optional map exists only because a dismiss has no child. A named child is held to the same rule assessionId, and an explicitnullis refused, not read as absent._acpmux/sidealways names itschat, so it stays in the strictscopedSessionParamsmap. A side frame withoutchatis still refused by the host, and this PR does not loosen it. - Local-state exposure: the reply is the daemon's ack and nothing else. No
replyShapesentry is added: only_acpmux/statushas one, and no write method of this kind does.
Testing
Shared parity cases (tests/cases/sessions.json, tests/cases/params.json) were committed first, before the fix:
- accepted, with the chat and the child in scope: passes
- child out of scope: refused
- chat out of scope: refused
- dismissed with no child: passes
nullchild: refused- unknown param: refused
Hosted focused verification (scripts/verify-cmux-tui-hosted.sh):
- Red on the cases commit:
session_scope ... FAILED, "offer_respond accepted with a child outside the scope is refused" (left: false, right: true). Run: https://github.com/manaflow-ai/cmux/actions/runs/38584000413 - Green on the fix:
session_scope ... ok: https://github.com/manaflow-ai/cmux/actions/runs/38586329657params_rule ... ok: https://github.com/manaflow-ai/cmux/actions/runs/38587986371the_allowlist_is_what_the_page_sends ... ok: https://github.com/manaflow-ai/cmux/actions/runs/38589430606
- I cancelled the unrelated macOS legs of these focused runs once the Linux
test (linux)job had reported. The runs therefore show as cancelled or failed overall, while theirtest (linux)jobs passed. - Release compile on the exact head (fleet,
check-release-compile.sh):** BUILD SUCCEEDED **. check-concurrency.shok.crash_ratchet.pyok.check-no-godfiles.shflags no touched file; every flag it raises is already on base.
Depends on #20305 for the daemon handler; merge after it.
Changelog
none
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Lets the agent pane's host allowlist accept _acpmux/offer_respond, which the page sends when a session offer chip is dismissed or accepted. The host previously refused the message because it was not allowlisted, so both Dismiss and Start failed.
Scoping rules
- The method is session-scoped on
sessionId; the chat holding the offer must be the pane's. - A new optional scoped-params map covers
childSessionId(Start only): an absent param passes, a present one must be a pane-held string, andnullis refused. _acpmux/sidestays strict; itschatparam is always required.- No gesture rule: Dismiss declines, and Start only links the page's own new chat, whose
session/promptalready requires a gesture.