acpmux: agent-to-agent messages show their sender; sub-sessions show in the transcript and summary
Agent-to-agent work in acpmux is now visible in the agent pane, and a lane's acpmux send report draws as a message from that lane, not as the person's prompt.
What changes
- Who sent a prompt. When a connection proves it runs inside an acpmux session (the caller token acpmux puts in that session's env) and prompts a different session without a provenance of its own, the daemon records
{kind: "session_message", from, fromName, to}on theuser_messageand on its queued record. The pane draws it as "Received message from ›", and the name opens that chat. The person's connections, prompts to the sender's own session, the acpmux TUI and a shell'sacpmux sendstay the person's. A provenance the sender gives is still checked and kept as before. This is the daemon-side fix the #22761 review asked for in place of a client-name guess. - Sub-session messages. A parent's
send_messagewaits behind the child's running turn by default (deliver: after_turn).next_stepis opt-in and delivers into the running turn when the child's agent can take a message mid-turn. The reply says which happened, with the queue's length. - What a child waits on.
get_sessionand the summary say what a sub-session waits on the person for (a question or a permission), plus its plan step and latest tool. - Where the user sees the child.
start_session's reply says the child is listed under the parent chat. - The child's chat names its sender (parent, sub-session, ancestor or descendant), with the sender's short name.
- Transcript and summary. Sub-session tool calls draw as cards (started, sent, waiting, reply). The summary lists sub-sessions after subagents, and a row opens the child's chat.
Tests
- Failing first:
cmux-tui/crates/acpmux/tests/subsessions_delivery.rs(e02cbe7, a parent's message waits behind a working child's turn) andtests/hub_integration/prompt_provenance.rsa_prompt_sent_from_inside_another_session_records_that_session_as_its_sender(bf479a0). Both are expected to fail on base and pass on the head. - Webview:
incomingMessage.test.ts,subsessionCalls.test.ts,subagents/subsessions.test.ts.
Observed / Not run
- Observed locally:
scripts/web-lint.shpassed,prepush-gate.pyfound no new violations, andcargo fmtis clean. - Not run: the Rust tests and the webview tests. Agents don't compile or run tests on Leo's Mac, so CI and the fleet are the first runs. The red-on-base and green-on-head results for the two failing-first tests come from CI.
- Not done: the Swift side (an open-tab action for a child chat, sidebar nesting). The Swift app also doesn't read
session_messageyet, so its own transcript still shows such a prompt as the person's.
No capture: the visible change is the agent pane's webview (sub-session cards, "Received message from …", the summary's sub-session rows), which the gallery advisory run captures; the only native path is build-agent-pane-web.sh adding the cards' stylesheet to the pane bundle.
Changelog
- Changed: Messages that agents send each other (sub-session messages and
acpmux sendfrom inside a session) show as "Received message from …" from the sending chat. Sub-sessions show as cards in the transcript and in the summary.
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Acpmux now shows agent-to-agent messages with their sender in the agent pane, and sub-sessions appear in the transcript and summary instead of reading as the person's own prompts.
- A prompt sent from inside another session with no provenance of its own is recorded as
session_messageand drawn as "Received message from "; sub-session family messages draw as coming from the sending chat, and the pair guard counts these. send_messagequeues behind the target's running turn by default (deliver: after_turn);deliver: next_stepis opt-in, and a child'snext_stepto its parent waits for the turn to end since a steer can cancel the parent's tool call. The reply says which happened and the queue length.- Running turns expose their current activity (latest tool title, plan step), and session summaries, session rows and
get_sessionreport what a sub-session waits on the person for (question or permission); an ancestor may answer a question when the person enables that. These fields are masked for peers and in rows handed to a parent's agent and stay optional in the protocol spec, as schemars emits them; session rows carry the summary's whole field set,watchingincluded. - Sub-session tool calls draw as cards in the transcript, including a running call in a live turn as its own card so the working line never names it, and a settled run holding a sub-session card stays unfolded. The summary lists sub-sessions under subagents; names open the child's chat.
Not included: Swift-side support (open-tab action, sidebar nesting); the Swift app still shows session_message prompts as the person's.