agent pane: a cmux_exec row says what its script did, and opens to JavaScript and a result tree
Leo, 2026-10-30: "these should be rendered better than just cmux called cmux exec we know what these are supposed to be"
Three cmux_exec scripts in a row each read "Called cmux · cmux exec", and an opened one showed the raw mcp__cmux__cmux_exec line, the script as one escaped JSON string and the response as escaped JSON. Now each row says what its script did, and opens to the script as JavaScript and the result as a tree.
What changed
-
Titles (
conversation/cmuxExec.ts). A finished script's title comes fromcall_login its answer ({calls, call_log, logs, result}, cmux-tuicli/mcp/code_mode.rs). A running script's title comes from thecmux.call("<name>")andcmux.<tool>(calls in its source, in the present tense.- One verb table:
workspace_list"Listed workspaces",app_agent_open/app_agent_open_tab"Opened N agent sessions",tab_create_browser/workspace_open_url"Opened N browser tabs",cmux.docs/cmux_docs"Searched cmux tools [for ]". It also covers pane, tab and terminal lists,workspace_createandworkspace_rename,tab_rename,tab_create_terminal,terminal_input_write,terminal_wait, terminal reads,pane_split,tab_close,tab_focus,workspace_status_setandnotification_create. - A change outranks a lookup. Counts are aggregated and pluralized.
- The workspace is named when the script or its result names exactly one. It is never named by id or by
current: "Opened 12 agent sessions in Leads". - Other steps add "and N more steps".
- A failed call counts only when none of its kind went through.
- Fallbacks:
- a generic
<noun>_<verb>name reads in the MCP rows' words ("Read workspace"); - otherwise "Ran cmux script (N calls): ", then "Ran cmux script";
- a malformed response falls back to the script's own calls.
- a generic
- One verb table:
-
One row per script. Consecutive scripts no longer fold into one "×3" line (
toolGroups.ts), so each reads by its own content. -
Opened (
McpCallRows.tsxCmuxExecDetails):- the script as JavaScript with real line breaks and coloured tokens (
jsHighlight.ts, synchronous, the pane's ANSI code roles); - the tools it called, with counts and failures;
- the result and any error as a key/value tree (
valueTree.ts): YAML-like, text unescaped with its line breaks, nested JSON text parsed, host-injected context dropped; - console logs.
A response with a number a double would round shows as it came. The
mcp__…name and the request and response as they came are behind Raw (the existing toggle the sub-session cards use). They are out of the default view but one click away. Find reads what the opened row draws. - the script as JavaScript with real line breaks and coloured tokens (
-
Known MCP tools covered.
cmux_exec,cmux_docs, and the cmux server's own tools called directly (workspace_*,pane_*,tab_*,terminal_*,screen_*,window_*,notification_*,app_*): their shapes are ours. Tools the server folds in from other servers (cua_*,subsessions_*,secrets_*,forum_*,acpmux_*) keep their own rows and cards. Every other MCP server keeps the generic line, and no other tool's row changes. The running line (toolPaths.ts) uses the same title. -
Cost. Titles, the script view and the opened parts are derived once per tool-call snapshot (WeakMaps on the immutable call, per language for titles). Nothing is derived per render or per streamed token. Nothing is parsed for a closed row beyond its title. This adds no O(rows) work per token.
-
52 new strings in
Localizable.xcstrings(21 languages, plurals per CLDR; non-Englishneeds_review). -
.github/design-surfaces.jsonmaps the transcript's tool-row files todesign/chat-transcript.md.
Spec: leo-workspace design/chat-transcript.md, section "cmux_exec and known MCP tools" (teamleaderleo/leo-workspace#1791).
Stills
Leo: "WE DONT NEED FUCKIGN SCREENSHOTS JUST LET ME TRY THE SHIT OUT". There are no stills; try the cmux script row in the nightly or dogfood build. The gallery state is agent-pane.cmux-exec-rows.
Tests
webviews/test/cmux-exec-rows.test.tsx(11 tests):- titles from
call_log, from a running script and from a fallback; - aggregation and failure counting;
- malformed responses;
- direct cmux tools against folded servers' tools;
- JS tokens and the value tree;
- three rows reading distinctly;
- the opened view: highlighted JS, a structured result, Raw keeping
mcp__cmux__cmux_exec; - Find text.
- titles from
- Unchanged and green:
agent-pane-mcp-tool-rows,toolGroups,ToolVerb,pane-english,agent-pane-locale,chat-find,subsessionCalls,transcript-density,tool-failureandsubsession-disclosure.
Observed / Not run
- Observed: the bun suites above, run locally (pass).
scripts/web-lint.sh: no findings in changed files. Its remaining failures (design tokens, layers, fmt, lint) are all inwebviews/src/pages/settings/*and are red on basead871573723.prepush-gate.py: no new violations.test/gallery-coverage.test.tshas 3 failures that are red on base and do not name this entry. - Not run: a Mac build (no Swift changed). WebKit gallery stills (Chromium only).
Changelog
- Changed: an agent's cmux script row says what it did ("Opened 12 agent sessions in Leads") and opens to the script and its result, not escaped JSON.
risk: normal (transcript rendering for one MCP server's tools only).
Size: about 1,450 lines, of which 52 are the string catalog's one-line entries, about 290 are the new test file and about 190 are the gallery entry. The renderer, its verb table, the tokenizer and the tree view are one feature with its tests, so this PR is not split.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Renders each cmux_exec row in the agent transcript so it says what its script did instead of "Called cmux · cmux exec", and opens to the script as highlighted JavaScript and the result as a tree rather than escaped JSON.
- A finished script's title comes from the
call_login its result; a running one's from thecmux.call(...)calls in its source. - Consecutive scripts no longer fold into one ×N row.
- An opened row shows the script as highlighted JavaScript, the calls with counts and failures, console logs, and the result and error as a YAML-like tree; the raw
mcp__cmux__cmux_execcall stays behind the existing Raw toggle. cmux_docsand the cmux server's own tools get the same titles; other MCP servers keep their current rendering.- Titles and opened parts are derived once per tool-call snapshot (WeakMaps), never per render or token, and Find reads what the opened row draws and highlights across its colored tokens.
- A cut call log (
call_log_truncated, or fewer entries than the reported count) never gives a wrong count: such a script's title says its total calls instead. - A number a double would round, in any JSON layer including a nested text block, shows the response as it came rather than parsed as a tree.
- A direct tool's cached title is reused only while its workspace name matches, so a workspace named by the run's later identifiers still appears in the title.