Skip to content

Commit 36cd58a

Browse files
author
OpenClaw Agent
committed
fix(cli): skip output-history replay on terminal resize
_resize_clear_ghosts (introduced in NousResearch#20444) replays _OUTPUT_HISTORY after every SIGWINCH to recover conversation content lost by the screen clear. This causes a jarring "re-streaming" effect — all recent output lines, including streaming tokens and spinner frames, are reprinted in rapid succession whenever the user maximizes/restores the terminal window (Command+Enter on macOS). Change _recover_after_resize to only clear the screen + reset the renderer, without replaying output history. prompt_toolkit's native _on_resize handler redraws the input area and status bar cleanly. Users who want to see conversation history restored after resize can still press Ctrl+L (/redraw), which calls _force_full_redraw (that path still includes _replay_output_history). Fixes the symptom reported in NousResearch#19280 where resize recovery replays entire conversation as if it were streaming from the beginning.
1 parent 839cdd1 commit 36cd58a

2 files changed

Lines changed: 15 additions & 3 deletions

File tree

cli.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,9 +2521,16 @@ def _clear_prompt_toolkit_screen(self, app, *, rebuild_scrollback: bool = False)
25212521
pass
25222522

25232523
def _recover_after_resize(self, app, original_on_resize) -> None:
2524-
"""Recover a resized classic CLI without desynchronizing cursor state."""
2524+
"""Recover a resized classic CLI without desynchronizing cursor state.
2525+
2526+
Only clears the screen and resets the renderer — does NOT replay
2527+
output history. Replaying on every resize causes jarring
2528+
"re-streaming" of entire conversation. Users who want history
2529+
restored can press Ctrl+L (/redraw) which calls _force_full_redraw.
2530+
"""
25252531
self._clear_prompt_toolkit_screen(app, rebuild_scrollback=True)
2526-
_replay_output_history()
2532+
# Deliberately NOT calling _replay_output_history() here.
2533+
# See docstring above.
25272534
original_on_resize()
25282535

25292536
def _schedule_resize_recovery(self, app, original_on_resize, delay: float = 0.12) -> None:

tests/cli/test_cli_force_redraw.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ def test_sends_full_clear_replays_then_invalidates(self, bare_cli, monkeypatch):
7272
]
7373

7474
def test_resize_rebuilds_scrollback_before_prompt_toolkit_redraw(self, bare_cli, monkeypatch):
75+
"""Resize recovery clears screen, rebuilds scrollback, resets renderer,
76+
then hands off to prompt_toolkit WITHOUT replaying output history.
77+
(Replay on every resize is too jarring — users can Ctrl+L instead.)
78+
"""
7579
app = MagicMock()
7680
out = app.renderer.output
7781
events = []
@@ -86,14 +90,15 @@ def test_resize_rebuilds_scrollback_before_prompt_toolkit_redraw(self, bare_cli,
8690

8791
bare_cli._recover_after_resize(app, original_on_resize)
8892

93+
# _recover_after_resize clears screen + resets renderer, then
94+
# calls original_on_resize directly — NO output history replay.
8995
assert events == [
9096
"reset_attrs",
9197
"erase",
9298
("raw", "\x1b[3J"),
9399
"home",
94100
"flush",
95101
"renderer_reset",
96-
"replay",
97102
"original_resize",
98103
]
99104
app.invalidate.assert_not_called()

0 commit comments

Comments
 (0)