Agent pane: an acpmux send is a "Sent message to <target>" row (shared with sub-session send_message)

Merged
TE
10d ago
+777-81

Stacked on #25045 (the acpmux send parser, live line and Background tasks); its own change is the transcript row. #24977 (sub-session cards) has landed, and this branch builds on it from feat-cmux-next. I'll land this after #25045.

Leo, 2026-10-24, word for word, looking at plain Bash rows (Ran acpmux "… and Running sed -i '' 's/This rule is…' 30s):

"These will be able to render as, like, I mean, not necessarily full renders of whatever, but, like, you know, sending message or sent message to X or Y instead of this thing, right? We're working on that?"

Leo, 2026-10-25, word for word, on a compound Bash call with an acpmux send in it that drew as a plain Bash row:

"Likewise in these situations, okay, I did see your message and whatnot, but like when you send messages in, I feel like, you know, those should be, like, rendered properly. You know, like, sent message to whatever"

What changes

  • A shell acpmux send is a message row in the transcript, not a command row (SentMessageLine.tsx; #25045's acpmuxSendCall decides what counts). While it runs it reads "Sending message to " with the working spinner. When it finishes it reads "Sent message to ", then the message's first line, muted. A --no-wait send also says how the daemon took it ("· queued", or "· read at its next step" when steered). One click opens the whole message as Markdown, and the raw command and its output (the Shell card) sit behind Raw. A failed send (non-zero exit) reads "Couldn't send to " and opens straight to its output.
  • A send inside a compound command gets the row too. When the command also did other work, the row says "Sent message to coord-deputy · and 2 more commands", and the whole command and its output are behind Raw: no work is hidden.
  • The target is a link to its chat when the session list has it (by id for a sub-session, else by name, host/name or host:name); it opens beside this chat through the existing SubsessionContext path. A target it can't resolve (a reports:person basket, an unlisted chat) is plain text.
  • Find in Chat opens the row and its Raw, and shows the whole output.
  • #24977's sub-session send_message row now draws through the same component, so both read "Sent message to tests" plus the first line, with #24977's delivery word and MCP Raw. There is one row component, not two. #24977's Raw toggle is now the shared RawToggle. A failed send_message reads "Couldn't send to ".
  • In toolGroups.ts the send is its own entry (type: "send"). It stays visible next to a folded Codex-style run's summary line, like the other coordination cards (TurnRows.tsx staysInSight, turns.ts ownRow). Find in Chat opens a row whose call holds the match. Whether a row is open persists across the virtualized transcript.
  • Gallery looks: the default is the sentence ("Sent message to tests "), which I picked because it is Leo's own wording. The arrow look (#24977's "→ tests: ") and the quote look ("Sent message to tests: “”") stay in the gallery behind the sentLook theme key.

Before / after

Gallery agent-pane.sent-message-rows (synthetic data), the finished turn opened. with-preview opens the here-document send; failed opens the refused send.

BeforeAfter
sent, dark
sent, light
with-preview, dark
with-preview, light
failed, dark
failed, light

Risk

Small and contained. Only the transcript's rendering changes. There are no protocol, daemon or native changes. A command that isn't exactly one send keeps its command row (#25045's parser), and a sub-session send that fails keeps the generic MCP row, as before. It adds no timers or observers. The one store subscription (the gallery look) is a module-level useSyncExternalStore, unsubscribed on unmount. The model.ts height estimate needs no change: the row is one tool-row line, like the command row it replaces.

Tests

  • SentMessageLine.test.tsx: a compound command's row ("· and 2 more commands", the command behind Raw), the target link by name and by host:name (and that it doesn't open the row), a reports: target as text; the sent row's sentence, delivery and preview, opening to Markdown and then Raw's Shell card; the running row (live, spinner); a send whose text comes from a file (target only); a failed send ("Couldn't send to perf-lane", opening to its output); and toolGroups giving a send its own entry between two commands.
  • Gallery agent-pane.sent-message-rows: sent (a plain --no-wait, one under timeout to host/name, a heredoc after cd, one through reach build-box 'perl -e "alarm 60; exec @ARGV" …', one whose text comes from $(cat …), and a sub-session send_message), with-preview, failed, running-row, look-arrow and look-quote. The fixtures use the event shapes Claude Code recorded through acpmux (Bash execute, the command cut to its title, the description, queued/steered from --no-wait) and acpmux's real no_session failure text, with made-up names and messages.

Agent session: cosmic-gray

🤖 Generated with Claude Code