acpmux: grouped tool calls and agent message cards
Part of the cmux-next parity work listed in #16174 (comment).
P0 items 4 and 5 (ACP UI parity): tool calls in the agent pane rendered one ad hoc row per call, and messages to other agents showed as raw bash or JSON.
What changes
Grouped tool calls (conversation/toolGroups.ts, ToolGroupRow.tsx, CommandRow.tsx)
- Consecutive calls of one kind fold under one line: "Ran 3 commands", "Read 4 files" (distinct paths), "Ran 2 searches", "Edited App.tsx +12 -3" or "Edited 3 files +40 -8". A lone edit with a diff still gets the edited verb and counts; any other lone call keeps its own row and title.
- An open group hangs its calls off a hairline tree guide under the group glyph. Each command row shows the command in mono, cut to the row, then a check or "Exit 2" and its run time ("840ms", "4.2s", "2m 5s"). It opens to its Shell block. Edit groups open to their diffs.
- A live group stays closed while calls join it, so the transcript height holds still. Its glyph spins while any call runs, and its line says "Running N commands".
- A settled run that is one group no longer folds twice: the group's line is the summary.
- Run time comes from new
startedAt/endedAton the tool model, stamped inmergeToolItemfrom the event times.
Agent message cards (conversation/agentMessages.ts, MessageCard.tsx)
tell-coordinator(--to,--from,--re, here-document bodies),cmux send --workspace/--surface, shell or file writes intoinbox/<name>/orcoordinator-inbox/<name>.jsonl, and Claude Code's SendMessage now render as a card: sender, arrow, recipient, channel, then the message's first line. Opening the card shows the whole message, plus the output if the send failed.- Command lines are split into steps and words with POSIX quoting, so a
;or&&inside a quoted message does not split it.
Localization: every new string, plus the previously hardcoded "Shell", "Exit code" and "failed", goes through t(), with en and ja entries.
Verification
bun test src/agent-session/acpmux: 617 pass, 0 fail. New suites:agentMessages.test.ts,toolGroups.test.ts,toolGroupRows.test.tsx. Updated the edit-fold transcript test for the new "Edited Total.swift+2-1" line.bun run checkis clean. The bundle was regenerated with./scripts/cmux-next/regenerate-web-bundles.sh.- Real captures against
ref/desktop-paritywill follow on this PR.
Localization audit: en and ja tables in acpmux/i18n.ts for all new tools.*, shell.* and message.* keys; i18n.test.ts keeps the tables complete. No Swift strings changed.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Groups consecutive tool calls in the acpmux agent pane into one line and draws messages to other agents as cards, so the transcript no longer shows one ad hoc row per call or raw bash and JSON.
- Consecutive commands, reads, searches, and edits fold under a summary line ("Ran 3 commands", "Read 4 files", "Edited App.tsx +12 -3") that opens to the calls on a tree guide; each command shows its exit status and run time, and edit groups count calls that carried no diff (deletes, moves).
tell-coordinator,cmux send, writes intocoordinator-inbox/<name>.jsonl, and Claude Code's SendMessage render as a sender-to-recipient card that clamps the message to two lines and shows a Show more toggle only when it overflows; only that one mailbox path counts, other folders namedinboxhold source, and rewrites of existing files stay edits.- A card draws only for a command that just sends a message; one that also pushes, builds, or tests stays a command row, while
cd,export, and pipedecho/catstill make a card, and piped text feedstell-coordinator -. - Run times use new
startedAt/endedAtfields stamped from event times, and command parsing follows the shell: quoted;or&&do not split, and file-descriptor redirects, background&,--name=valueoptions, and--helpare handled. tell-coordinatoroptions end at the text's first word as the script reads them, assignments before the program are skipped,cmux send --helpsends nothing, an MCPsend_messagetool is not a card, and an object SendMessage body renders as JSON.- Localized the new strings plus the previously hardcoded "Shell", "Exit code", and "failed" with en and ja entries, and regenerated the agent pane bundle.