Agent pane: an acpmux send is a "Sent message to <target>" row (shared with sub-session send_message)
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 sendis a message row in the transcript, not a command row (SentMessageLine.tsx; #25045'sacpmuxSendCalldecides 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-waitsend 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/nameorhost:name); it opens beside this chat through the existing SubsessionContext path. A target it can't resolve (areports:personbasket, 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_messagerow 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'sRawtoggle is now the sharedRawToggle. A failedsend_messagereads "Couldn't send to ". - In
toolGroups.tsthe 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
arrowlook (#24977's "→ tests: ") and thequotelook ("Sent message to tests: “”") stay in the gallery behind thesentLooktheme 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.
| Before | After | |
|---|---|---|
| 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 byhost:name(and that it doesn't open the row), areports: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); andtoolGroupsgiving a send its own entry between two commands.- Gallery
agent-pane.sent-message-rows:sent(a plain--no-wait, one undertimeouttohost/name, a heredoc aftercd, one throughreach build-box 'perl -e "alarm 60; exec @ARGV" …', one whose text comes from$(cat …), and a sub-sessionsend_message),with-preview,failed,running-row,look-arrowandlook-quote. The fixtures use the event shapes Claude Code recorded through acpmux (Bashexecute, the command cut to its title, the description,queued/steeredfrom--no-wait) and acpmux's realno_sessionfailure text, with made-up names and messages.
Agent session: cosmic-gray
🤖 Generated with Claude Code











