acpmux: Take over holds the managing agent's messages until Hand back

Closed
TE
6d ago
+374-30

Leo asked for a ChatGPT-style "X is managing this chat [Take over]" on chats that Chief or another agent runs. #30765 adds the banner; this PR makes Take over actually stop the manager, in acpmux. This is item 3 of the agent group-chats plan.

What changes

  • New method _acpmux/agent_hold {sessionId, held} (capability agent-hold-v1).

    • held: true (Take over) tags the chat acpmux.agentHold. From then on, the pair guard (hub/agent_pairs.rs) holds every message from the chat's manager as a pause with reason taken_over. Held messages are kept across restarts, as the guard already does.
    • held: false (Hand back) removes the tag and delivers the held messages in order.
  • Manager means:

    • the session that started the chat (startedBy, spawnedBy),
    • its delegator,
    • or, for a chat a Chief started (mux.parent = optchat-chief[:<home>]), any turn of that Chief (cmux.chief = <home>).

    Other agents' messages pass as before.

  • The person's own prompts into a taken-over chat run, and release nothing. agent_pair_resume_inner(by: "person") skips pairs that a hold keeps.

  • Only the person can take over or hand back. The method requires local_person and is refused from web origins (remote_guard). _acpmux/tag refuses the hold tag to anyone but the person (person.rs), so a manager cannot lift its own hold.

  • The hold applies even when the pair guard is switched off.

  • Wired through rpc.rs, protocol/mod.rs and the spec json, capabilities.rs, requests.rs, agent_messages.rs and peer_forward.rs (daemon-own).

  • Pane policy:

    • policy.json (gesture always, params sessionId, held) and its test cases.
    • The Swift mirror (AcpmuxPaneMethods*.swift).
    • The webview's generated methods.ts.
    • direct.ts agentHold(held).
  • Wiring the banner's Take over to agentHold (plus a Hand back line) is a follow-up once #30765 lands.

Tests

  • tests/hub_integration/agent_pairs.rs:
    • a_taken_over_chat_holds_its_managers_messages_until_handed_back:
      • An agent cannot take over.
      • The Chief's messages are held with trip taken_over.
      • Another agent passes.
      • The person's prompt runs and releases nothing.
      • The manager can't lift the tag.
      • Hand back delivers 3 messages in order.
      • Later messages pass.
    • another_chiefs_turn_is_not_the_manager.
  • Fleet (cmux-ci test):
    • cargo test -p acpmux --test hub_integration agent_pairs: 26 passed.
    • cargo test -p acpmux --lib: 2572 passed.
    • cargo test -p cmux-agent-pane-policy: all pass, including the_allowlist_is_what_the_page_sends.
    • cargo clippy -p acpmux --all-targets -D warnings: clean.
    • cargo fmt: clean.
    • Webview bun run check: 0 lint errors, no type errors.

Risk

acpmux message delivery (state machine). It changes behaviour only for chats carrying the new tag, which only the person sets. The pair guard's paths for untagged chats are unchanged, apart from the guard-off early return moving below sender resolution.

Agent session: agent session

šŸ¤– Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Implements Take over / Hand back for agent-run chats in acpmux: the person can now stop a managing agent's messages on a chat and hold them until handing the chat back.

New Features

  • _acpmux/agent_hold {sessionId, held} (capability agent-hold-v1) tags the chat acpmux.agentHold; the pair guard then holds the manager's messages as a taken_over pause, kept across restarts, and held: false delivers them in order.
  • The manager is the session that started the chat, its delegator, or the Chief that started it; other agents' messages and the person's own prompts pass as before, and a prompt releases nothing.
  • Only the person on this machine can take over or hand back, the hold applies even when the pair guard is off, and the manager cannot lift the tag.
  • Wired through rpc, the protocol spec, pane policy, the Swift mirror, and the webview (agentHold in direct.ts); wiring the take-over banner is a follow-up.