Skip to content

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks - #36864

Merged
ethernet8023 merged 1 commit into
NousResearch:mainfrom
maxmilian:fix/tui-reset-terminal-input-modes-on-exit
Jun 2, 2026
Merged

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks#36864
ethernet8023 merged 1 commit into
NousResearch:mainfrom
maxmilian:fix/tui-reset-terminal-input-modes-on-exit

Conversation

@maxmilian

@maxmilian maxmilian commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

When the TUI exits via Ctrl+C, SIGTERM/SIGHUP, or a crash, prompt_toolkit's teardown can be bypassed, leaving DEC 1004 (focus reporting) and 1000/1002/1003 (mouse tracking) enabled. The terminal then emits raw ESC[I / ESC[O focus events and fragmented SGR mouse reports as visible text in whatever runs next in the same tab.

Fixes #36823.

Root cause

_TERMINAL_INPUT_MODE_RESET_SEQ already exists and is emitted by _recover_terminal_input_modes — but only reactively, mid-session, when leaked mouse reports are detected. Nothing emits it on exit. On a clean teardown prompt_toolkit restores the modes itself; on Ctrl+C / signal / crash that unwind is skipped and the modes leak.

Fix

_run_cleanup() — the once-only cleanup that runs on every exit path (it's atexit-registered and also called explicitly on normal/EOF/interrupt exit) — now calls a new _reset_terminal_input_modes_on_exit() as its final step. The helper writes _TERMINAL_INPUT_MODE_RESET_SEQ to sys.stdout guarded on isatty(). The disable codes are no-ops for any mode that was never enabled, so emitting them unconditionally on a TTY is safe and idempotent.

Scope / known limitation: SIGKILL is uncatchable, so kill -9 mid-run still leaves the modes set — nothing in-process can prevent that. This fix covers normal quit, Ctrl+C, and SIGTERM/SIGHUP, which are the catchable paths.

Tests

tests/cli/test_tui_terminal_reset_on_exit.py (4, all pass):

  • emits the full reset sequence (incl. ESC[?1004l) and flushes on a TTY
  • no-ops when stdout is not a TTY (won't pollute piped/redirected output)
  • swallows stdout errors (cleanup must never raise at teardown)
  • is wired into _run_cleanup (the catch-all exit path)

ruff check clean; scripts/check-windows-footguns.py clean; existing tests/cli import sanity passes.

Infographic

tui-exit-terminal-mode-reset

@maxmilian
maxmilian force-pushed the fix/tui-reset-terminal-input-modes-on-exit branch from 16409b8 to 5216c7e Compare June 1, 2026 15:24
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 1, 2026
…leaks

When the TUI exits via Ctrl+C, SIGTERM/SIGHUP, or a crash, prompt_toolkit's
teardown can be bypassed, leaving DEC 1004 (focus reporting) and 1000/1002/1003
(mouse tracking) enabled. The terminal then emits raw ESC[I/ESC[O focus events
and fragmented SGR mouse reports as visible text in whatever runs next in the
same tab.

_run_cleanup() — the once-only cleanup that runs on every catchable exit path
(atexit-registered + called on the normal/EOF/interrupt exit) — now emits
_TERMINAL_INPUT_MODE_RESET_SEQ (the same disable sequence the in-session leak
recovery already uses) as its FIRST step, so the terminal is usable immediately
on Ctrl+C and a later teardown step raising can't skip it.

The reset is gated on a new _tui_input_modes_active flag (set right before
app.run(), cleared once the modes are disabled) so non-TUI one-shot CLI runs —
which share _run_cleanup via atexit — don't emit codes for modes they never
enabled. Writes to sys.stdout when it's the terminal, else falls back to
/dev/tty. SIGKILL is uncatchable and the kanban worker's os._exit(0) bypasses
atexit, but both are non-TTY/non-TUI so there is nothing to reset there.

Adds tests/cli/test_tui_terminal_reset_on_exit.py (9): emits on a TTY when the
TUI ran, no-ops when the TUI never ran, /dev/tty fallback when stdout is
redirected, no-op when neither is available, swallows stdout errors, flag set
and cleared, and wired into _run_cleanup as the first step even when a later
step raises.

Fixes NousResearch#36823

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@maxmilian
maxmilian force-pushed the fix/tui-reset-terminal-input-modes-on-exit branch from 5216c7e to 038ed94 Compare June 1, 2026 15:27
@maxmilian
maxmilian marked this pull request as ready for review June 1, 2026 15:44

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

✅ Looks Good

  • Fixes a real UX issue: terminal input modes (focus reporting + mouse tracking) leaking into next shell session
  • Properly gated on _tui_input_modes_active flag so one-shot non-TUI CLI runs don't emit escape codes
  • Written as first step in _run_cleanup (atexit) so it runs even if later cleanup raises
  • Falls back to /dev/tty when stdout is redirected
  • Graceful error handling: all exceptions swallowed (cleanup must never raise)
  • Comprehensive tests: 7 test cases covering TTY, non-TTY, /dev/tty fallback, stdout errors, flag lifecycle, and cleanup wiring
  • Known limitation documented: kill -9 and os._exit(0) paths bypass atexit

Reviewed by Hermes Agent

@ethernet8023
ethernet8023 merged commit a6b6afd into NousResearch:main Jun 2, 2026
23 checks passed
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…rminal-input-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…rminal-input-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…rminal-input-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…rminal-input-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…rminal-input-modes-on-exit

fix(cli): reset terminal input modes on TUI exit to stop focus/mouse leaks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TUI leaves focus-reporting mode enabled on exit — raw escape sequences leak into terminal

4 participants