Agent pane: a message going out reads "Sending message to <target>" (acpmux send parser, live line, Background tasks)

Merged
TE
11d ago
+1270-20

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?"

This is the first of two PRs. This one reads a shell acpmux send from its command line and names it on the turn's live line (the Running … 30s line in Leo's screenshot) and in Background tasks. The second PR (stacked on #24977 and this one) draws the transcript row itself, "Sent message to X" plus the first line, which expands to the Markdown and the raw command.

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

  • acpmuxSends.ts reads a command line the way a shell does. It handles single and double quotes, $'…', backslashes, here-documents (<<'EOF', <<-) and here-strings, $(…) and backticks kept whole, comments, and ; && || | &. It sees through these wrappers: reach HOST '…' (the target then reads HOST/name), bash -lc '…', timeout N, env X=Y, X=Y, cd dir &&, perl -e 'alarm N; exec @ARGV', nohup, nice, command, exec, and cmux acp send. Options follow acpmux send --help, so --status ID and --help send nothing. The text counts as unknown when it comes from stdin the line doesn't contain, or when it contains an expansion. Targets are the word as given (name, host/name, host:name, reports:person). There is no list of names. Only a command in command position counts: echo "acpmux send x y", grep 'acpmux send' and acpmux ls are not sends. A send anywhere in a compound command (;, &&, ||, a pipeline, a subshell, a heredoc or file on stdin) is still a send: the parser counts the other commands that do work (others, e.g. git push && acpmux send … is one more; so is a redirect that writes a file or a substitution that does more than read), so the row can say "and N more commands" instead of hiding them. Two sends, a send inside a substitution, an unclosed quote, or an exit before the send are not a send row. cd, export, an echo, || true, 2>&1, >/dev/null and filters on the send's own output that read and write no file (| tail -1, | jq -r .text; not | tee out or | sed …) don't count as other work. An option acpmux send --help doesn't list means no send; acpmux --json send … is one.
  • The live line for a send in flight reads "Sending message to " instead of Running acpmux send --no-wait coord-deputy '…'. That covers a shell send and a cmux-subsessions send_message, which shows the sub-session's name once the session list has it. This works in both the Codex-style line (Thinking.tsx) and the todo-list activity line.
  • Session Info's Background tasks and the running tray name a background send "Sending message to X", "Sent message to X" or "Couldn't send to X".

Before / after

Gallery agent-pane.sending-line shell (synthetic data), a turn whose in-flight call is acpmux send coord-deputy '…':

BeforeAfter
Live line, dark
Live line, light

Risk

Small and contained. Only the webview's labels change. There are no protocol, daemon or native changes. A command the parser can't read as exactly one send keeps its current label. The parser is a single pass over the command text (O(length)). It runs only for a call whose line contains acp, once per live-line derivation, and never per transcript row. It adds no timers, observers or animation loops.

Tests

  • acpmuxSends.test.ts covers every wrapper above, quoting (single, double with escapes, $'…', backslashes, spliced apostrophes, newlines in quotes), here-documents (quoted, unquoted with an expansion, <<-), a here-string, piped echo and cat <<EOF |, options before, between and after the words, --status and --help, the target forms, and the non-matches: acpmux ls, echo "acpmux send x y", grep 'acpmux send', a heredoc body that mentions it, a second send (also inside a substitution), other work around it, file-writing redirects, substitutions that run more than readers, filters that read or write files, an unknown option, and a perl script that only mentions exec @ARGV. A 5000-step pipeline after the send parses in one pass. Compound commands: a send after cd && git fetch and before a reach, one in a subshell with || echo, --from/--no-wait with a file on stdin (target only) and with a heredoc after git push, and acpmux ls; echo acpmux send (no send). It also checks the call's state (running, sent, failed on a non-zero exit) and the Background tasks label.
  • Gallery agent-pane.sending-line: shell, reach and subsession. The fixtures use the exact event shapes Claude Code recorded through acpmux (Bash execute calls, the command cut to its title, a description), with made-up names and messages.

Agent session: cosmic-gray

🤖 Generated with Claude Code