Repository navigation
acpmux, agent pane: a question answered in the pane counts on another machine - #23869
teamleaderleo wants to merge 7 commits into
Conversation
…proof over its answers (fails: answers are never covered) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
… machine (the proof covers the answers) Answering an agent's question in the Mac pane for a chat on an ssh peer was refused by the peer as "accepted only from the cmux app": a relayed decision proof never covered question answers. The pane now signs the answers too (acpmux-person-decision-answers-v1, the sha256 of the canonical answers JSON as a phone's signed answer has it), and acpmux counts such an answer only under that proof, so a relay still cannot swap the answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Unclear: CI failed on
Failures:
Matched log linesNot re-run automatically: Written by red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 44fc3d3 to compare; do not merge until green; see lane-rules Gallery before/after for
States
Pick other states with a |
|
Found 47 test failures on Blacksmith runners: Failures
...and 37 more test failures. View all on Blacksmith: run 39841951723, run 39841951809, run 39841951667
|
|
|
|
|
|
Owned by hb-acpmux on big-red · session |
…re ticket (fails: no ticket is taken with answers) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
…s pick ignores the answers, so the host signs it for another machine (review of #23869); the test key parses before it allocates Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
|
Review (independent subagent): SHIP at 8028cf3. Fleet compile-check --app passed at that head (job b352dc53c1eaf8fe20455a0d). Reds at this head are base: crash-class rules (CEFFileDragRelay.swift:162 selector probe), web bundles/react-apps-check (inspector/ContextSection.css missing from build-agent-pane-web.sh), CI guard group (workflow-guard-tests); none of those files are in this diff. |
…aemon with its answers RemoteAcpmuxWire drops a relayed answer's answers before agent-session-permission, and the daemon's verb refuses the field, so that machine's acpmux checks the answers proof against a plain allow and refuses it. Red at this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
RemoteAcpmuxWire passed a relayed answer's option and proof to the owning session daemon but dropped its answers, and agent-session-permission had no answers field, so that machine's acpmux verified the answers proof against a plain allow and refused the answer (Codex review of #23869). The wire now hands the answers to AgentSessionRemoteClient.permission, AgentSessionAttachClient sends them, and the daemon passes a JSON object of at most 64 KiB to acpmux unchanged; acpmux still checks them against the question and the proof over them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bdd-880a-7a72-b582-c0a285babfd6&host=cosmic-gray
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Orphaned: no activity for 15 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged |
|
|
|
|
|
|
|
|
|
|
|
Re-cut as #25424, rebased onto feat-cmux-next. This branch isn't under the adopting lane's push prefix, so the work moves there. Credit for the work stays with this PR. Adopted by hb-acpmux after the stale-pr-reconciler flagged this PR as orphaned. |
…hine carries the person proof over its answers and annotations Re-cut of #23869 (cosmic-gray lane), pane side; the daemon side is its own PR. The pane signs a relayed answer with acpmux-person-decision-answers-v1 over its answers and annotations, its gesture ticket binds those (by the sha256 of their canonical bytes), and the remote wire and the session daemon client carry both to that machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er the person proof over its answers and annotations Re-cut of #23869 (cosmic-gray lane), daemon side; the pane side is its own PR. A relayed AskUserQuestion answer was refused because the person decision proof (cx-0q7o) never covered answers. acpmux-person-decision-answers-v1 covers the answers digest, and the annotations digest when there are any; annotations from a connection that is not the person count only under it. cmux-tui agent-session-permission carries answers and annotations to acpmux unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hine carries the person proof over its answers and annotations Re-cut of #23869 (cosmic-gray lane), pane side; the daemon side is its own PR. The pane signs a relayed answer with acpmux-person-decision-answers-v1 over its answers and annotations, its gesture ticket binds those (by the sha256 of their canonical bytes), and the remote wire and the session daemon client carry both to that machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er the person's proof (re-cut of #23869, daemon side) (#25424) * acpmux: a question answered in the pane counts on another machine under the person proof over its answers and annotations Re-cut of #23869 (cosmic-gray lane), daemon side; the pane side is its own PR. A relayed AskUserQuestion answer was refused because the person decision proof (cx-0q7o) never covered answers. acpmux-person-decision-answers-v1 covers the answers digest, and the annotations digest when there are any; annotations from a connection that is not the person count only under it. cmux-tui agent-session-permission carries answers and annotations to acpmux unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux tests: rustfmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…hine carries the person's proof (re-cut of #23869, pane side) (#25440) * agent pane: a question answered in the pane for a chat on another machine carries the person proof over its answers and annotations Re-cut of #23869 (cosmic-gray lane), pane side; the daemon side is its own PR. The pane signs a relayed answer with acpmux-person-decision-answers-v1 over its answers and annotations, its gesture ticket binds those (by the sha256 of their canonical bytes), and the remote wire and the session daemon client carry both to that machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: the answers proof extension is nonisolated, so the gesture pick (nonisolated) can take a digest The Release compile refused canonicalDigest from AgentPaneGestureIntent and AgentPaneGesturePick: the extension took the module default (main actor). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane tests: no nested #require (recursive macro expansion), and the presence test client takes answers and annotations Rebased onto feat-cmux-next, which added RemoteAcpmuxPresenceTests (#25375) with its own AgentSessionRemoteClient. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Answering an agent's question in the Mac app's chat pane failed for a chat on an ssh peer (Leo hit it live on huge-blue): the peer's acpmux refused it with "approve this on the Mac app: allowing a permission is accepted only from the cmux app".
Cause
A chat on another machine gets its answers through a connection that is never the person, so the peer counts an allow only with the person's decision proof (cx-0q7o). That proof never covered question answers:
person_decidedandrespond_permissionrefused any proof whenanswerswas present. Every AskUserQuestion answered in the pane for a peer chat was refused. The rule dates from the design (9c4543d), not a recent change.Fix
acpmux-person-decision-answers-v1covers the answers too: the allow's five parts plus the sha256 of the canonical answers JSON, the same digest a phone's signed answer uses (approvers::answers_digest). An answer counts only under that proof, so a relay still cannot swap the answers; a plain allow keeps the v1 proof. Nonces are still spent once, under the prompt's lock.signRelayedAllowsigns a frame's answers with the bound key (AcpmuxSecretKey.decisionProof(..., answers:), canonical JSON byte-for-byte as acpmux's).direct.ts), and the Swift pick no longer skips frames with answers (AgentPaneGestureIntent.swift). Without the ticket the host never signed the answer, so the first fix alone did not reach the peer (the review caught this).The ticket covers the click on the option, not the answers themselves: the page fills the answers in after the person's click, which is the same trust the local daemon already gives the page. The proof then binds those exact answers, so nothing past the host can change them.
answeredByis not covered by the proof, as with plain allows.The peer must run an acpmux with this change; an older peer keeps refusing the same way.
Tests
cargo test -p acpmux --test person_allow a_relayed_answer_counts_with_the_persons_proof_over_exactly_its_answersandthe_answers_decision_proof_matches_the_apps_vector: red on f078763 (the test commit; the answer is refused, the vector does not compile), green on the head.cd webviews && bun test src/agent-session/acpmux/questionAnswerTicket.test.ts: red at bf80b32 (fleet step ff550a72710e1b8e2ae50508: "a question's answer carries the person's ticket for its session, prompt and option" fails), green at 8028cf3 (0 failed).AgentPaneRelayedAllowTestsanAnswerToAnotherMachinesQuestionCarriesTheProofOverItsAnswersandtheAnswersProofMatchesAcpmuxsVector.Observed / Not run
cmux-ci compile-check --apppassed at 8028cf3 (job b352dc53c1eaf8fe20455a0d). Independent review: SHIP at 8028cf3.scripts/cmux-next/prepush-gate.pypasses; rustfmt--checkis clean. The Rust vector was computed independently with Python (HMAC-SHA256 over the canonical JSON).cmux-next crash-class rules(CEFFileDragRelay.swift:162needs a selector probe),cmux-next web bundlesandreact-apps-check(inspector/ContextSection.cssis missing frombuild-agent-pane-web.sh), andcmux-next CI guard group(workflow-guard-tests on workflow files). None of those files are in this diff.No capture: the change is in the proof and the gesture plumbing, and the question card looks the same.
Changelog
Fixed: Answering an agent's question in a chat on another machine no longer fails with "accepted only from the cmux app".
risk:high: this changes what the person's decision proof covers (auth).
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes answering an agent's question in the Mac app's chat pane for a chat on another machine, which the peer's acpmux always refused with "accepted only from the cmux app".
A relayed allow's decision proof never covered question answers. The pane now signs an answer with
acpmux-person-decision-answers-v1— the allow's five parts plus the sha256 of the canonical answers JSON — and acpmux counts an answer only under that proof, so a relay cannot swap the answers. An answer also takes the person's gesture ticket for its session, prompt and option, as an option pick does.The answer's answers now reach that machine's acpmux:
RemoteAcpmuxWireandagent-session-permissionpreviously dropped or refused them, so the peer checked the answers proof against a plain allow and refused the answer. The daemon passes a JSON object of at most 64 KiB to acpmux unchanged.The peer must run an acpmux with this change; an older peer still refuses the answer.
Written for commit 5afde6f. Summary will update on new commits.