Skip to content

feat: rework the TUI transcript into collapsible episodes - #217

Open
arelchan wants to merge 1 commit into
mainfrom
feat/tui_episode_transcript
Open

feat: rework the TUI transcript into collapsible episodes#217
arelchan wants to merge 1 commit into
mainfrom
feat/tui_episode_transcript

Conversation

@arelchan

Copy link
Copy Markdown
Contributor

Summary

Reworks the TUI transcript so a turn reads as a sequence of steps instead of
interleaved thinking blocks and tool lines. Every model call becomes one
"episode" holding its reasoning, narration and tools.

Rendering

  • A step with no narration collapses to one summary line ("reasoning for 8s,
    read 2 files"); a narrated or running step shows a reasoning fold, the
    narration as prose, then its tool rows.
  • Reasoning expands while it streams and folds once the step produces anything
    visible (narration, a tool, or the answer text).
  • A run of same-name calls renders as a tree: one header plus a child row per
    call, so a burst of parallel searches no longer fills the screen.
  • In-flight tools report elapsed time; finished ones keep their measured span.
  • Rows are bound to the transcript width and clipped with an ellipsis. Without
    the bound, long details overflowed the container and the terminal soft-wrapped
    them, which looked like stray blank lines and edge-cut text.

Tool display contract

Tools now own how they appear. Tool.display_call and the new ToolResult
(model_text plus optional display_text) separate what the model reads from
what the transcript shows:

  • ask_user renders its question and the answer instead of a raw arguments
    blob, pairing each question with its answer when several are asked at once.
  • Tools with no explicit verb derive a readable one from their own name, so a
    newly added tool renders sensibly with no changes to the verb table (it is an
    overrides list, not a required registry).
  • The call preview picks the query, question or command out of the arguments
    instead of the first value, which could be a numeric flag.

Also

  • Episodes are the default transcript style; /transcript legacy stays as an
    escape hatch and the legacy path is untouched.
  • An interrupt commits the collapsed episode view instead of dumping the raw
    expanded segments.
  • The virtual-height estimate accounts for episode rows. Without it the
    transcript reserved too few rows and left stale cells on screen.
  • Markdown table columns fit the available width with CJK-aware clipping.
  • The deep-research offer states that a regular search is the user's chosen
    path, not a failure, so the model stops narrating it as a broken fallback.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

cd ui-tui && npm run type-check          # clean
cd ui-tui && npm test                    # 927 passed | 3 skipped
cd ui-tui && npm run lint                # 0 errors, no warnings in touched files
cd ui-tui && npx prettier --check "src/**/*.{ts,tsx}"   # clean
uv run ruff check raven/ tests/          # All checks passed
uv run ruff format --check raven/ tests/ # 758 files already formatted
uv run pytest tests/ -k "spine or tui_rpc or ask_user or deep_research or agent_loop" \
  --ignore=tests/integration             # 725 passed

New tests cover the episode commit path (including interrupt), the tool summary
and verb fallback, the argument preview, and the rendered layout.

  • Relevant tests pass locally
  • Relevant lint / type checks pass locally
  • User-facing docs or screenshots are updated when needed

Risk

  • Security impact considered
  • Backward compatibility considered
  • Rollback path is clear for risky changes

The wire gains one optional tool.start field (display); older payloads
without it fall back to the generic preview. execute may now return either a
string or a ToolResult, so existing tools are unaffected. Rollback is
/transcript legacy for the view, or reverting this commit.

Known follow-ups, not addressed here: occasional screen garbling seen when
messages are queued (root cause not yet identified), and web_search being
registered even without an API key.

Related Issues

N/A

Group every model call into one episode (its reasoning, narration and tools)
and render a turn as a flat stream of steps instead of interleaved thinking
blocks and tool lines.

Rendering:
- A step with no narration collapses to one summary line ("reasoning for 8s,
  read 2 files"); a narrated or running step shows a reasoning fold, the
  narration as prose, then its tool rows.
- Reasoning expands while it streams and folds once the step produces anything
  visible (narration, a tool, or the answer text).
- A run of same-name calls renders as a tree with one header and a child row
  per call.
- In-flight tools report elapsed time; finished ones keep their measured span.
- Rows are bound to the transcript width and clipped with an ellipsis, so long
  details no longer overflow the container and soft-wrap.

Tool display contract:
- Tool.display_call and the new ToolResult keep the model-facing text separate
  from what the transcript shows. ask_user now renders its question and the
  answer instead of a raw arguments blob, pairing each question with its answer
  when several are asked at once.
- Tools with no explicit verb derive a readable one from their own name, so a
  newly added tool renders sensibly without touching the table.
- The call preview picks the query, question or command out of the arguments
  rather than the first value, which could be a numeric flag.

Also:
- Episodes are now the default transcript style; "/transcript legacy" stays as
  an escape hatch.
- An interrupt commits the collapsed episode view instead of dumping the raw
  expanded segments.
- The virtual-height estimate accounts for episode rows; without it the
  transcript reserved too few rows and left stale cells on screen.
- Markdown table columns fit the available width with CJK-aware clipping.
- The deep-research offer states that a regular search is the user's chosen
  path, not a failure, so the model stops narrating it as a fallback.

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
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.

1 participant