Skip to content

fix(tui): truncate collapsed generic tool summaries - #60363

Open
neriousy wants to merge 1 commit into
devfrom
tool-summary-truncate
Open

neriousy wants to merge 1 commit into
devfrom
tool-summary-truncate

Conversation

@neriousy

@neriousy neriousy commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Issue for this PR

Fixes #48783

Note: #48783 is assigned to @simonklee.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Root cause: GenericTool renders its collapsed summary through genericToolSummary and primitiveInputSummary, which concatenated every primitive argument as key=${String(value)} without eliding long values or bounding the total display width. Calling any plugin or MCP tool with a multi-kilobyte string argument dumped the entire string into the collapsed transcript row, soft-wrapped across dozens of lines, even though GenericTool already renders the full arguments behind its click-to-expand toggle.

Fix:

  • Normalize whitespace/newlines per primitive argument and elide each value at 40 display columns with Locale.truncateWidth in primitiveInputSummary, so a large payload argument no longer hides trailing short arguments (such as format=markdown).
  • Bound the total summary display width with Locale.truncateWidth (defaulting to 120 columns, and sized to the available session row width ctx.width - 3 - INLINE_TOOL_ICON_WIDTH in GenericTool and ExecuteCallView) while keeping the closing ].
  • Pass truncate from GenericTool through InlineTool / InlineToolRow / Spinner (wrapMode="none" truncate) so the collapsed row never soft-wraps onto a second line during resize or when a scrollbar narrows the viewport.

How did you verify your code works?

  • Added unit tests in packages/tui/test/util/tool-display.test.ts covering multi-kilobyte multi-line values alongside short arguments, many-argument width bounds, and CJK / ZWJ emoji grapheme boundaries.
  • Added an end-to-end OpenTUI session regression test in packages/tui/test/generic-tool-summary.test.tsx (--rerun-each 10) verifying single-line collapsed rendering at both 100 and 44 columns and full untruncated input/output expansion on click.
  • Ran bun typecheck and bun run typecheck:tsc in packages/tui and bun run check from the repo root.
  • Captured before/after visual proofs with opencode-drive against origin/dev and this branch.

Visual

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 72" width="240" height="72" fill="none">
  <style>
    .mono { font: 10px ui-monospace, SFMono-Regular, Menlo, monospace; fill: currentColor; }
    .muted { opacity: 0.62; }
    .reply { animation: shift 5s ease-in-out infinite; }
    .details { opacity: 0; animation: expand 5s ease-in-out infinite; }
    @keyframes expand {
      0%, 45%, 92%, 100% { opacity: 0; }
      55%, 82% { opacity: 1; }
    }
    @keyframes shift {
      0%, 45%, 92%, 100% { transform: translateY(0); }
      55%, 82% { transform: translateY(24px); }
    }
    @media (prefers-reduced-motion: reduce) {
      .reply, .details { animation: none; }
    }
  </style>
  <rect x="8" y="8" width="2" height="12" fill="currentColor" opacity="0.5" />
  <text x="15" y="17" class="mono">Ingest architecture notes.</text>
  <text x="15" y="34" class="mono muted">&#x2713; ingest_document [content=# Hallaz&#x2026;, format=md]</text>
  <g class="details">
    <text x="25" y="46" class="mono muted">content:</text>
    <text x="76" y="46" class="mono"># Hallazgos del 2026-09-13 &#x2014; Section 01&#x2026;</text>
    <text x="25" y="57" class="mono muted">format:</text>
    <text x="76" y="57" class="mono">markdown</text>
  </g>
  <g class="reply">
    <text x="15" y="50" class="mono">Document ingested into index.</text>
  </g>
</svg>

Screenshots / recordings

Before / After (opencode-drive):

  • Before (origin/dev): Calling a dynamic ingest_document plugin tool with a ~7.5 KB multi-line content string and format: "markdown" floods the entire 100×30 TUI viewport with soft-wrapped content=... lines in the collapsed state, pushing both the user prompt and the ✓ ingest_document header off screen.
  • After (this branch): The collapsed row stays on a single tidy line (✓ ingest_document [content=# Hallazgos del 2026-09-13 — Architectu…, format=markdown]) directly between the user prompt and assistant response, and clicking the row expands the full multi-kilobyte content, format, and output in place.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #54703

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I reproduced #48783 on dev with the issue's script: the collapsed summary is 5,429 characters and holds the whole document. With this change it is 66 characters and ends in …]. I also ran your new generic-tool-summary.test.tsx against the base: it fails, and the captured screen shows the content= row wrapping across the whole viewport. With the PR it passes 5 runs out of 5. The unit tests and tsc -b in packages/tui pass.

Public API: unchanged (TUI rendering only).

The new caps also apply to mini mode's rows, since they use primitiveInputSummary too. That seems fine. I couldn't get a plugin tool call to show up in opencode-drive here, so I have no screenshots. Could you attach the before/after captures you mention in the description?

Related: #54703

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: V2 TUI generic tool summary prints full tool input on the collapsed line (untruncated)

1 participant