Summary
When a tool card (execute_code, terminal, write_file, etc.) is unfolded, the arguments are unreadable — the user sees roughly a line and a half of wrapped, indentation-stripped, word-broken garbage instead of the actual code. Reported by @cygnus on Discord (May 02 2026):
"wish list item — when things like execute-code are folded, and I unfold them, I do it because I want to see the code, but I can only see a line and a half of it"
Same bug surfaced a second time the same morning when Cygnus tried to debug a recurring macOS Terminal permission prompt and couldn't read the executing command:
"I'm trying to figure out what code keeps on giving me this several times in a row, but I can't expand and see the code it's running when it does"
Both reports are the same root cause.
Root cause (verified)
Bug 1 — .tool-arg-val has no white-space: pre-wrap
static/ui.js:3974-3977 renders args like this:
${tc.args&&Object.keys(tc.args).length?`<div class="tool-card-args">${
Object.entries(tc.args).map(([k,v])=>`<div><span class="tool-arg-key">${esc(k)}</span> <span class="tool-arg-val">${esc(String(v))}</span></div>`).join('')
}</div>`:''}
The val is wrapped in an inline <span> inside a <div>. Current CSS (static/style.css:1700-1701):
.tool-arg-key{color:var(--blue);font-family:'SF Mono',ui-monospace,monospace;font-size:var(--font-size-xs);}
.tool-arg-val{color:var(--muted);font-family:'SF Mono',ui-monospace,monospace;font-size:var(--font-size-xs);word-break:break-all;}
Default white-space: normal means every \n collapses to a single space, every run of whitespace collapses to one space, and indentation is destroyed. Then word-break: break-all chops the resulting prose-ified blob at arbitrary character boundaries to fit the card width.
For a 30-line Python script passed as the code argument to execute_code, this renders as one wrapped run-on string of ~1.5 visible lines (depending on viewport width and font size). The user sees something like:
code import os, sys sys.path.insert(0, '/home/...') from foo import bar def main(): try: ...
Instead of the actual indented multi-line code.
Bug 2 — .tool-card-result pre caps too tight
static/style.css:1702:
.tool-card-result pre{...max-height:240px;overflow-y:auto;...}
The parent .tool-card-detail opens to max-height: 600px (line 1697). The 240px cap on pre means tool output can also feel cramped even though the outer card has room. Less severe than Bug 1 because the inner pre does have its own scrollbar, but still a paper cut.
Fix
Two CSS-only changes. ~10 LOC.
Patch (static/style.css:1698-1702)
.tool-card-args{margin-bottom:6px;}
.tool-card-args > div{font-size:var(--font-size-xs);line-height:1.6;display:flex;gap:6px;align-items:flex-start;}
.tool-arg-key{color:var(--blue);font-family:'SF Mono',ui-monospace,monospace;font-size:var(--font-size-xs);flex-shrink:0;}
.tool-arg-val{
color:var(--muted);
font-family:'SF Mono',ui-monospace,monospace;
font-size:var(--font-size-xs);
white-space:pre-wrap; /* preserve newlines + leading whitespace */
word-break:break-word; /* wrap on word boundaries, NOT character */
flex:1;
min-width:0; /* allow flex item to shrink below content width */
max-height:360px;
overflow-y:auto;
display:block;
margin:0;
}
.tool-card-result pre{
font-size:var(--font-size-xs);
color:var(--muted);
font-family:'SF Mono',ui-monospace,monospace;
white-space:pre-wrap;
word-break:break-word; /* was: break-all — see Bug 1 reasoning */
max-height:480px; /* was: 240px — better fit inside 600px parent */
overflow-y:auto;
margin:0;
line-height:1.55;
}
Why these specific values
white-space: pre-wrap on .tool-arg-val — preserves every newline and leading space, exactly what users want when reading code. Doesn't affect short single-line args (they still render as one line).
word-break: break-word instead of break-all — only breaks long unbroken tokens (URLs, base64) at character boundaries, not normal code. break-all was actively harmful: it'd wrap import os mid-word at imp\nort os.
max-height: 360px on val + overflow-y: auto — for genuinely huge args (a 200-line code blob), card stays bounded but val gets its own scrollbar inside. Card-detail outer cap is 600px, so 360 leaves room for key, output, and chrome.
max-height: 480px on result pre — bumps from 240px so long outputs are more useful inside the 600px card. Still capped to keep the card itself manageable.
display: flex on the row + flex-shrink: 0 on key + flex: 1; min-width: 0 on val — keeps the key column from being compressed when val wraps.
What this is NOT
- Not a JS change. The renderer is fine; it's the CSS that misbehaves.
- Not a "verbosity setting" — Cygnus speculated this might be configurable; it isn't, and shouldn't need to be. The default should just work.
- Not removing the height cap entirely — at 600px parent + scroll-when-needed, the user can read what they want without the page becoming an unbounded code dump.
Reproducer
- Run any session that calls
execute_code with a multi-line script (most agent sessions hit this within 1-2 turns).
- Tool card collapses by default.
- Click chevron to expand.
- Observe args region:
code value is one wrapped run-on line of ~1.5 visible lines, indentation gone, breaks at arbitrary characters.
After fix:
- Click chevron to expand.
- Observe args region:
code value is a properly indented, line-preserved code listing inside its own scrollable region (capped at 360px).
Tests
Behavioral test in tests/test_tool_card_args_rendering.py:
- Render a tool card with a multi-line
code arg, assert the rendered HTML contains the literal \n characters intact (escaped properly, but present in the DOM text).
- Computed-style smoke test (jsdom or Playwright):
getComputedStyle(.tool-arg-val).whiteSpace === 'pre-wrap', wordBreak === 'break-word'.
- Visual regression check: render with a 30-line code arg, snapshot the bounding-box height of
.tool-card-detail is reasonable (between ~200px and 600px), not overflowing the parent.
~50 LOC of tests.
Files
static/style.css — the patch above (~15 LOC)
tests/test_tool_card_args_rendering.py — new (~60 LOC)
Estimated effort
~75 LOC total. Single PR. P1 — UX paper cut on the most common interaction (looking at a tool call) that affects every user every session.
Reporter / context
- @cygnus, Discord #testers-talk, May 02 2026
- Two reports same morning (the wishlist phrasing + the recurring-permission-prompt frustration both have the same fix)
- No prior issue
Summary
When a tool card (
execute_code,terminal,write_file, etc.) is unfolded, the arguments are unreadable — the user sees roughly a line and a half of wrapped, indentation-stripped, word-broken garbage instead of the actual code. Reported by @cygnus on Discord (May 02 2026):Same bug surfaced a second time the same morning when Cygnus tried to debug a recurring macOS Terminal permission prompt and couldn't read the executing command:
Both reports are the same root cause.
Root cause (verified)
Bug 1 —
.tool-arg-valhas nowhite-space: pre-wrapstatic/ui.js:3974-3977renders args like this:The val is wrapped in an inline
<span>inside a<div>. Current CSS (static/style.css:1700-1701):Default
white-space: normalmeans every\ncollapses to a single space, every run of whitespace collapses to one space, and indentation is destroyed. Thenword-break: break-allchops the resulting prose-ified blob at arbitrary character boundaries to fit the card width.For a 30-line Python script passed as the
codeargument toexecute_code, this renders as one wrapped run-on string of ~1.5 visible lines (depending on viewport width and font size). The user sees something like:Instead of the actual indented multi-line code.
Bug 2 —
.tool-card-result precaps too tightstatic/style.css:1702:The parent
.tool-card-detailopens tomax-height: 600px(line 1697). The 240px cap onpremeans tool output can also feel cramped even though the outer card has room. Less severe than Bug 1 because the innerpredoes have its own scrollbar, but still a paper cut.Fix
Two CSS-only changes. ~10 LOC.
Patch (
static/style.css:1698-1702)Why these specific values
white-space: pre-wrapon.tool-arg-val— preserves every newline and leading space, exactly what users want when reading code. Doesn't affect short single-line args (they still render as one line).word-break: break-wordinstead ofbreak-all— only breaks long unbroken tokens (URLs, base64) at character boundaries, not normal code.break-allwas actively harmful: it'd wrapimport osmid-word atimp\nort os.max-height: 360pxon val +overflow-y: auto— for genuinely huge args (a 200-line code blob), card stays bounded but val gets its own scrollbar inside. Card-detail outer cap is 600px, so 360 leaves room for key, output, and chrome.max-height: 480pxon resultpre— bumps from 240px so long outputs are more useful inside the 600px card. Still capped to keep the card itself manageable.display: flexon the row +flex-shrink: 0on key +flex: 1; min-width: 0on val — keeps the key column from being compressed when val wraps.What this is NOT
Reproducer
execute_codewith a multi-line script (most agent sessions hit this within 1-2 turns).codevalue is one wrapped run-on line of ~1.5 visible lines, indentation gone, breaks at arbitrary characters.After fix:
codevalue is a properly indented, line-preserved code listing inside its own scrollable region (capped at 360px).Tests
Behavioral test in
tests/test_tool_card_args_rendering.py:codearg, assert the rendered HTML contains the literal\ncharacters intact (escaped properly, but present in the DOM text).getComputedStyle(.tool-arg-val).whiteSpace === 'pre-wrap',wordBreak === 'break-word'..tool-card-detailis reasonable (between~200pxand600px), not overflowing the parent.~50 LOC of tests.
Files
static/style.css— the patch above (~15 LOC)tests/test_tool_card_args_rendering.py— new (~60 LOC)Estimated effort
~75 LOC total. Single PR. P1 — UX paper cut on the most common interaction (looking at a tool call) that affects every user every session.
Reporter / context