Repository navigation
perf(terminal): silence replies provoked by replay and coalesce keystrokes - #749
Merged
Merged
Conversation
Refs #745 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
…rokes Attaching an inline terminal wrote up to 512 KiB of replay into xterm at once, and xterm answered every terminal query embedded in it — cursor position reports, device attributes, colour queries — through onData, each forwarded to the provider as its own IPC invoke: one attach measured 21,795 session:input calls in 15 s with a 7.4 s renderer long task, and main wrote thousands of stale ESC[row;colR replies into the agent's stdin. A shared forwarder now holds a latch while xterm parses replayed content and drops whatever it provokes, coalesces same-tick chunks into one IPC call, and only acknowledges user activity for data that was forwarded. All three xterm hosts use it. Fixes #745 Refs #103, #390 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
…d prove the host path Review of #749: a synchronous throw from term.write (xterm's pending-bytes watermark) left replayDepth raised for chunks that would never call back, which is a pane that drops every keystroke for the rest of its life. The executor now releases the unqueued chunks' share and rejects. The host test's xterm mock never invoked the write callback, so it modelled exactly that stuck latch; it now calls back and asserts that a keystroke typed after attach reaches sendInput. A unit test covers xterm's synchronous callback fast path, and the inline debug terminal joins its backlog like the leaves do. Refs #745, #751 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
Owner
Author
|
Review applied:
|
This was referenced Sep 3, 2026
Juliusolsson05
added a commit
that referenced
this pull request
Sep 3, 2026
…tings instead of unmounting it MainSurface rendered the focus-takeover surfaces instead of the GlobalEditorShell subtree, so entering Reader Mode with terminal panes disposed every xterm and detached every PTY, and leaving re-attached and replayed up to 512 KiB into each brand-new terminal — a multi-second freeze and lost scrollback per toggle. Global Editor fullscreen already retains the workspace under display:none and tells the terminal ownership boundary that mounted no longer means visible; RetainedWorkspaceSurface applies the same pattern one level up for the surfaces that bypass the shell, with a layout-neutral display:contents wrapper when visible. TileLeaf folds the new hidden flag into its Tail-All mask so un-hiding is a genuine false→true transition that re-pins, and the shell's fullscreen Escape listener stands down while hidden so Escape in Reader Mode exits Reader rather than also collapsing a fullscreen editor underneath. Fixes #752 Refs #745, #749 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Attaching an inline terminal writes up to 512 KiB of replay into xterm in one
write. xterm answers every terminal query embedded in that stream — cursor-position reports (ESC[6n), device attributes, OSC colour queries — throughterm.onData, and the three xterm hosts forwarded each chunk to the provider with its ownipcRenderer.invoke. Today's performance journal: one attach at 20:18:57 → 21,795session:inputcalls in 15 s and a 7,448 ms renderer long task; other attaches → 2,071 and 2,236 calls; a steady ~5 Hz trickle while any pane is attached. Main then wrote thousands of staleESC[row;colRreplies into the agent's stdin — answers to questions the provider asked minutes ago about a screen it has since repainted. Details in #745.What changed
src/renderer/src/workspace/tile-tree/terminalInputForwarder.ts:replay(term, chunks)writes the replay and holds a latch until xterm's write callback confirms each chunk is parsed — everythingonDataemits meanwhile is dropped;onData(data)outside a replay coalesces same-tick chunks into onesendInput(one microtask of latency).AgentTerminalLeaf,TerminalLeafandAgentInlineTerminaluse it for both the buffer replay and the pre-attach backlog, and acknowledge user activity only for data that was actually forwarded (a replay reply is not interaction).Keystrokes typed during the ~100 ms replay window are dropped with the replies — the pane has not painted yet, and a stale reply cannot be told from a real key (both are escape sequences).
Linked issues
Fixes #745. Refs #103, #390.
Verification
terminalInputForwarder.test.ts(unit): replies emitted while a replay is parsed are dropped and nothing is sent; live data after the replay is forwarded, coalesced per tick in order; an empty replay resolves without holding the latch; multi-chunk replay keeps order and releases only after the last callback. 4 tests passed.--project renderer src/renderer/src/workspace/tile-tree/— passing;npx tsc -b— clean.session:inputbursts onsession:agent-pty-attachshould disappear from the journal and the renderer long task with them.Not in scope
Answering DSR/DA in the headless terminal so responses are consistent whether or not a pane is attached, and switching keystroke transport to a fire-and-forget
send(both noted in #745).Plan:
docs/superpowers/plans/2026-09-03-terminal-replay-input-storm.md.Known limitations
session:inputper repaint burst (~5 Hz per attached pane) and still callacknowledgeSession, which clears the unread/ACTION badge without user interaction. Tracked in perf(terminal): live TUI query replies still cost one session:input each and clear the unread badge #751; the structural fix is answering DSR/DA in the headless terminal so the renderer never has to.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa