perf(terminal): stream output and history - #8564
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Warning Your free Security trial is over. An organization admin can activate Security or dismiss this notice. Comment |
There was a problem hiding this comment.
One finding on the new web scrollback-top replay upgrade. Everything else in the in-scope web files (Ghostty core/surface chunked writes, Home/End viewport handling, drawer output-cursor wiring, ChatView drawer unmount) looked consistent with the existing primitives and Tailwind ownership rules.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This is a broad terminal architecture change spanning PTY backpressure, history persistence, transport protocol events, shared state, and web/native rendering, with substantial new runtime behavior. It also changes default history, buffering, scrollback, and presentation settings, so the impact warrants human review. You can add or adjust custom eligibility rules. Learn more. |
There was a problem hiding this comment.
One finding on the new Ghostty write queue in apps/web/src/terminal/ghostty/surface.ts. The extended-replay viewport restore raised in the previous run now looks addressed by pendingScrollbackReplayIdentityRef + scrollToTopAfterWrites, so that is not re-reported.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding: requesting the extended scrollback replay clears the rendered terminal before the replacement history arrives. Details inline.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding in apps/web/src/terminal/ghostty/surface.ts: the write queue can hold a replay chunk behind a live write, and core.writeReplay throws once the replay has been closed at that boundary, which permanently stalls the rAF drain. Details inline.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding in the web terminal renderer: an extended-replay attach that restarts (e.g. after a WebSocket session change) is no longer treated as a streamed replay, which blanks the viewport and replays retained history through the live write path.
Posted via Macroscope — UI Consistency
df0b417 to
a982d15
Compare
There was a problem hiding this comment.
One finding on the new Ghostty write scheduling: the rAF-gated write queue has no size bound and cannot drain while the window is hidden.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding on the web terminal renderer: the default attach path now retains only one server output batch of client-side output, so a busy terminal falls back to a full ghostty_terminal_reset replay. Details inline.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding in the web renderer: the new per-frame write budget is paired with an unbounded write queue, so sustained PTY output above ~4 MB/s makes the visible terminal fall behind the session instead of janking while staying current. Everything else previously flagged on this PR (blank-screen resets at replay boundaries, replay chunks queued behind live writes, rAF-only draining in hidden windows, retention vs. attach-replay budgets) reads as addressed in the current head.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding on terminal theming ownership; the rest of the changed web surface (streamed replay wiring in ThreadTerminalDrawer, write queue bounds, ChatView unmount) looks consistent with the existing contracts.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Two theme-ownership issues in the new alternate-screen terminal palette. Everything else in the web scope (streamed replay ordering, bounded write drain, scrollback-top request, renderer default remapping) looks consistent with the existing surface contracts.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding: the resize repaint is now deferred to an animation frame, which lets a frame composite the stale canvas backing store scaled into the new element box.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Two findings in the web terminal renderer geometry and link detection introduced by fix(web): align terminal interaction geometry. Details inline.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a08dae3. Configure here.
There was a problem hiding this comment.
Reviewed the web terminal changes (ThreadTerminalDrawer.tsx, ghostty/{core,surface,renderer}.ts, terminal-links.ts, index.css). One finding: the drawer's streamed-replay path discards the replay/live classification that readTerminalOutputUpdate returns, so live output batched with a replay is fed into Ghostty with the PTY writer detached.
Posted via Macroscope — UI Consistency
|
Superseded by #9027, which carries this branch's feature work rebased onto current main plus a hardening pass on the replay delivery protocol (marker version-skew gating, completion latching, byte-bounded attach buffering, chunk compaction, and stream-restart epochs). Closing to restart review on the consolidated diff. |

Large terminal sessions repeatedly rebuilt retained output in server and client memory, rewrote growing log files, and could leave slow remote subscribers with unbounded queued deltas. The small attach snapshot limited scrollback, reopening a shell could redraw its prompt through an unchanged PTY resize, and full-screen apps such as
btopvisibly tore because synchronized updates were painted between PTY chunks.This makes terminal output a bounded stream end to end:
node-ptyaround a 2 MB low-water markhost:portendpointsbtop, vim, htop, less, tmux, and similar TUIs; source-to-target remapping preserves reverse video and leaves the primary screen plus explicit ANSI/OSC colors untouchedCtrl+Cas ETX before Kitty keyboard mode and verifiesCtrl+Rightword navigation through the real shellMeasured on this checkout:
btop -u 100at 150x37: largest synchronized update was 32,507 bytes across roughly six raw PTY callbacks; the fixed renderer painted at a 99.4 ms median interval, matching the requested 100 ms application cadenceVerification:
btoprenderingbtop -u 100→ light-mode shell and light-mode shell → vim → light-mode shell switch without breakup or residual dark rowsbtopowns the alternate screen immediately restores the canvas and mount torgb(252,252,252)rgb(70,70,70)selected versusrgb(13,13,13)unselected), then exits to a fully light canvas and mountbtopresize frames (817↔857 px): zero blank frames, stable visible-content density, and a fully light canvas after exitfile:line:columnhighlighting remains modifier-gated while hostname and IPv4host:portendpoints stay unlinkedVisual verification:
The repository already uses the latest stable
node-pty1.1.0. The 1.2.0-beta.15 line remains prerelease and has a reported Windows regression where persistent PTYs can exit before their first write, so this PR does not take that native dependency risk. The newer Ghostty source needs regenerated WASM, iOS, and Android artifacts plus cross-platform ABI/performance testing, so that vendor refresh remains separate. iOS, Android, and Windows native runtime profiling still require their platform toolchains. Mobile benefits from the 64 KB event ceiling and existing display-frame coalescing, but its native Ghostty APIs do not yet expose mode 2026 for protocol-level gating of updates larger than 64 KB.Authored by GPT-5.6 Sol with the Codex harness through T3 Code.
Note
Stream terminal output and history with byte-based batching and backpressure
historyTargetBytes,historyMaxBytes, replay variants) and adds output coalescing, chunked persistence, and PTY pause/resume backpressure inTerminalManager.replay-start/replay-completestream events and chunked replay delivery (≤64 KB chunks), with a newreadSnapshotmethod for subscriber resync on backpressure overflow in ws.ts.TerminalOutputChunksegments with retention budgets, incrementalreadTerminalOutputUpdatecursors, and replay/live delivery tags in terminalSession.ts.TerminalViewportand mobileNativeTerminalSurfaceto consume structured output via streaming commands (write,writeReplay,reset), with on-demand extended replay (4 MiB) when scrolling to top.TerminalManagerOptionsremoveshistoryLineLimit; any caller passing that option will fail. Theattachlistener signature gains a requireddeliveryparameter, andTerminalAttachInputnow validatesreplayByteswithin[64 KiB, 8 MiB].Macroscope summarized f459a54.
Note
High Risk
Changes span PTY lifecycle, attach streaming protocol, durable history I/O, and native/mobile rendering paths—any ordering or backpressure bug can lose output, duplicate history, or break interactive shells.
Overview
Terminal sessions stop passing one giant
bufferstring through attach, persistence, and native renderers. Output is retained and delivered as byte-bounded chunks with replay vs live provenance, and the server coalesces PTY output, appends durable history, and pauses the PTY when subscribers fall behind.Server:
TerminalManagerreplaces line caps with separate durable (≈8–12 MB) and client-replay budgets, batches output (~8 ms / 64 KB), bounds the pre-drain queue (4 MB) with optionalpauseOutput/resumeOutput, and wraps attach inreplay-start→ snapshot → chunked history →replay-completebefore live events. Extended attach can request larger replay viareplayBytes;attachStreamlisteners get adeliveryflag (replay|live), and slow WS clients get a fresh snapshot instead of an unbounded delta queue. AddsreadSnapshotand ignores duplicate resizes.Clients: Shared state moves from
terminal.buffertoTerminalOutputStatewithreadTerminalOutputUpdatedriving incrementalwrite/writeReplay/reseton mobile (revision-gatedstreamingRevision) and ordered replay-then-live rendering on web (writeTerminalOutputSegments, extended replay when scrolling to top). Native Ghostty scrollback limits switch from row counts to 64 MB byte budgets; replay feeds suppress PTY query replies from echoing to the shell.UI polish: Hidden web terminal drawers unmount instead of staying mounted; buffer replay during font changes becomes an explicit
replayPausedflag; terminal link detection gains unambiguousfile:line:columnpatterns without treatinghost:portas paths; alternate-screen theme CSS variables support full-screen TUIs.Reviewed by Cursor Bugbot for commit f459a54. Bugbot is set up for automated code reviews on this repo. Configure here.