fix(cli): recover terminal state after interrupt to prevent raw control sequence freeze#34064
Open
zccyman wants to merge 1 commit into
Open
Conversation
…ol sequence freeze When the agent is interrupted during processing, prompt_toolkit's renderer and VT100 input parser can be left in an inconsistent state. CSI 6n cursor position report responses leak as literal text (^[[19;1R) and the terminal stops accepting keyboard input. Fix: in process_loop's finally block, after an interrupted turn: - flush_stdin() to drain stray escape bytes from the OS input buffer - _force_full_redraw() to reset prompt_toolkit's renderer cache Closes NousResearch#33271
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.
Summary
When the agent is interrupted during processing (busy_input_mode=interrupt, the default), the terminal can become completely unresponsive. Raw CSI 6n cursor position report responses leak as literal text (
^[[19;1R), and no further keyboard input is accepted.Closes #33271
Problem
The
process_loopdaemon thread'sfinallyblock (afterchat()returns) only calledapp.invalidate()to refresh the status line. When an interrupt occurred:CSI 6ncursor position queryThe only recovery was killing the terminal tab/process.
Solution
In
process_loop'sfinallyblock, after an interrupted turn (_last_turn_interrupted):flush_stdin()— drain stray escape-sequence bytes from the OS input buffer usingtermios.tcflush(TCIFLUSH), preventing them from corrupting the next input cycle_force_full_redraw()— reset prompt_toolkit's renderer cache and force a clean repaint, recovering from any cursor/screen state driftBoth operations are wrapped in try/except and are no-ops when stdin is not a TTY.
Files Changed
cli.pyprocess_loopfinally blocktests/cli/test_terminal_interrupt_recovery.pyTest Results
Existing
tests/cli/test_cli_force_redraw.py: 9 passed (zero regression)Full
tests/cli/: 673 passed, 1 pre-existing failure (unrelated)Design Decisions
Condition on
_last_turn_interrupted— Only trigger recovery after an actual interrupt, not on every normal turn completion. This avoids unnecessary screen flicker on the common path.flush_stdinbefore_force_full_redraw— Drain stray bytes first so they don't arrive during the redraw and corrupt it. Order matters.Graceful degradation — If
flush_stdinfails (no TTY, non-POSIX), we still attempt the full redraw. Both operations are independently safe.