Skip to content

fix(tui): prefer raw text over rendered ANSI in TUI message display#16392

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/tui-rendered-priority-16391
Closed

fix(tui): prefer raw text over rendered ANSI in TUI message display#16392
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/tui-rendered-priority-16391

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

Fixes #16391 — TUI garbles output when final_response_markdown: render is active.

When this config key is set, the gateway sends a payload.rendered field containing Rich-generated ANSI output (cursor movement \x1b[A, line clear \x1b[K, colour codes). The Ink-based TUI renderer does not support these complex escape sequences but turnController.ts had rendered taking priority over text in two places:

  • recordMessageComplete (line 426): payload.rendered ?? payload.text ?? this.bufRef → picked ANSI over raw markdown, Ink rendered it as garbled overlapping text.
  • recordMessageDelta (line 519): rendered ?? this.bufRef + text → replaced the accumulated streaming buffer with an incomplete ANSI fragment on every chunk, discarding all prior text.

Changes

  • recordMessageComplete: flipped priority to payload.text ?? payload.rendered ?? this.bufRef so TUI always gets raw markdown.
  • recordMessageDelta: removed the rendered ?? branch; since text is already guard-checked above (line 515), we always accumulate cleanly via this.bufRef + text.

Tests

Two new cases added to createGatewayEventHandler.test.ts:

  • message.complete prefers text over rendered to avoid ANSI garble in TUI
  • message.delta accumulates raw text and ignores rendered ANSI fragments

Both pass; no pre-existing tests broken (7 failures due to missing dist/ink-bundle.js build artifact are pre-existing on main).

Platforms affected

TUI only (hermes --tui). CLI and gateway platforms handle Rich ANSI correctly via their terminal emulators — this change does not affect them since rendered is never injected into those paths.

TUI's Ink renderer cannot handle Rich ANSI control sequences (cursor-up
\x1b[A, line-clear \x1b[K) emitted when final_response_markdown: render
is active. Two sites in turnController.ts had the rendered/text priority
inverted:

- recordMessageComplete picked payload.rendered first, passing ANSI
  escape sequences to Ink which garbled them as overlapping coloured text.
- recordMessageDelta replaced the accumulated text buffer with an ANSI
  fragment on every streaming chunk, discarding prior text and injecting
  a mid-sequence escape string.

Fix: prefer payload.text in recordMessageComplete and always accumulate
raw text in recordMessageDelta, leaving rendered unused in the TUI path.

Closes NousResearch#16391

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 27, 2026 05:30

Copilot AI 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.

Pull request overview

This PR fixes garbled output in the Ink-based TUI when display.final_response_markdown: render causes the gateway to include Rich-generated ANSI in payload.rendered, by ensuring the TUI prefers raw markdown (payload.text) for both complete and streaming messages.

Changes:

  • Prefer payload.text over payload.rendered in TurnController.recordMessageComplete.
  • Always accumulate streaming output from payload.text in TurnController.recordMessageDelta (ignore rendered fragments).
  • Add Vitest coverage to ensure complete/delta events prefer/accumulate raw text over ANSI-rendered output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ui-tui/src/app/turnController.ts Changes message buffering to prioritize raw text over ANSI rendered for TUI safety.
ui-tui/src/tests/createGatewayEventHandler.test.ts Adds regression tests validating text preference and streaming accumulation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

this.bufRef = rendered ?? this.bufRef + text
this.bufRef = this.bufRef + text

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

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

recordMessageDelta no longer uses rendered, but the parameter destructuring/type still includes it. Consider removing rendered from the signature to avoid implying it affects buffering/streaming behavior.

Copilot uses AI. Check for mistakes.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Apr 27, 2026
After the raw-text-preferred change, rendered is no longer read inside
the function. Remove it from the destructure so the signature reflects
actual behaviour and callers are not misled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briandevans

Copy link
Copy Markdown
Contributor Author

@copilot Finding addressed in commit ec4131c4a:

Finding (line 519 — unused rendered parameter): Removed rendered from the destructure signature. The function only reads text; carrying rendered in the type implied it affected buffering/streaming behavior, which it no longer does. Callers continue to pass payloads that may contain rendered (TypeScript structural typing allows extra fields on non-literal arguments), so no call-site changes were needed.

@briandevans

Copy link
Copy Markdown
Contributor Author

Closing — already on main as @OutThisLife's #17111 (commit 8d591fe). The merged change lands the exact same swap in recordMessageComplete (payload.text ?? payload.rendered) and recordMessageDelta (drop rendered from the streaming buffer accumulation), and explicitly references issue #16391 in its comment. Their PR also adds three test cases I didn't (raw-text-on-complete, fallback-to-rendered-when-text-missing, accumulate-text-and-ignore-rendered).

Thanks @OutThisLife.

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

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] TUI garbles output when final_response_markdown: render — rendered/text priority inverted

3 participants