fix(keyboard): clear ISIG/IEXTEN in raw mode to prevent SIGTSTP suspension#1545
fix(keyboard): clear ISIG/IEXTEN in raw mode to prevent SIGTSTP suspension#1545n-WN wants to merge 1 commit intoMoonshotAI:mainfrom
Conversation
…nsion The keyboard listener's Unix raw mode only cleared ICANON and ECHO, leaving ISIG and IEXTEN set. This caused Ctrl+Z to generate SIGTSTP and suspend the process (with no handler to recover), particularly problematic when using IME + tmux where accidental Ctrl+Z is easy. Changes: - Clear ISIG and IEXTEN in raw-mode lflags, consistent with prompt_toolkit's tty.setraw() behavior - Add CTRL_C key event so Ctrl+C (now a byte, not a signal) can cancel the run in the _LiveView fallback path - Handle CTRL_C alongside ESCAPE in _LiveView.dispatch_keyboard_event - Add 18 tests covering raw-mode flags, byte→event mapping, and cancel-event dispatch
There was a problem hiding this comment.
🟡 Windows keyboard listener missing CTRL_C byte handling added to Unix listener
The PR adds \x03 → KeyEvent.CTRL_C mapping to _listen_for_keyboard_unix at src/kimi_cli/ui/shell/keyboard.py:190-191, but the corresponding _listen_for_keyboard_windows function (lines 256-275) was not updated with the same case. This means on Windows, when using the standalone _LiveView visualization path, pressing Ctrl+C will never emit KeyEvent.CTRL_C, so the cancel-via-Ctrl+C feature (src/kimi_cli/ui/shell/visualize.py:1188) cannot trigger. Users on Windows would have to rely on ESC to cancel. The existing pattern shows both listeners should handle the same set of bytes (both already handle \x05 for CTRL_E).
(Refers to lines 262-263)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1351de3ed
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # handle ESC / Ctrl+C to cancel the run | ||
| if event in (KeyEvent.ESCAPE, KeyEvent.CTRL_C) and self._cancel_event is not None: | ||
| self._cancel_event.set() |
There was a problem hiding this comment.
Route Ctrl+C through question panels as a cancel
Because dispatch_keyboard_event() returns from the question-panel branch before reaching this new CTRL_C cancellation path, pressing Ctrl+C during a QuestionRequest in _LiveView is now a no-op. That path is used whenever visualize() runs without a prompt session (src/kimi_cli/ui/shell/visualize.py:103-119), e.g. command-mode runs from Shell.run() (src/kimi_cli/ui/shell/__init__.py:163-167). In those runs, question-based tools treat an empty response as “dismissed” rather than cancelled (src/kimi_cli/tools/ask_user/__init__.py:120-126, src/kimi_cli/tools/plan/__init__.py:198-205), so users cannot abort the active run with Ctrl+C once a question panel is visible.
Useful? React with 👍 / 👎.
|
To solve some key interaction problems, this PR is not ready yet, and may be related to other issues, which are being planned. plz @RealKai42 Do not merge for now. |
|
FYI, we should also consider the behavior on windows |
Related Issue
Resolves #38 —
can not handle CTRL+Z singal(reported by @yihong0618, closed as wontfix but never actually fixed).Also related: #1480 (flow:skill not interruptible in interactive mode — the
_LiveViewpath where this bug lives).Description
The raw keyboard listener (
keyboard.py) used in the standalone_LiveViewvisualization path only clearedICANONandECHOwhen entering raw mode, but leftISIGandIEXTENset. WithISIGactive, the kernel intercepts control characters before they reach the application:Ctrl+Z→SIGTSTP→ process suspended (no handler installed → hung process)Ctrl+C→SIGINT→ process killedThis does not affect the normal interactive shell, which uses
prompt_toolkit(it correctly clearsISIGviatty.setraw). The bug manifests in the_LiveViewpath — activated whenprompt_sessionisNone(e.g., piped/non-interactive runs, or when an IME interferes with tmux prefix detection causing raw keys to reach the app).Fix
keyboard.py(2 lines changed):ISIGandIEXTENalongsideICANONandECHOwhen configuring raw mode, so all control characters are delivered as bytes instead of signals.\x03(Ctrl+C) to a newKeyEvent.CTRL_Cenum value.visualize.py(1 line changed):CTRL_Cthe same asESCAPEindispatch_keyboard_event— set the cancel event to abort the active run.Before / After (verified in tmux)
ISIGflagIEXTENflagCtrl+Z\x1a→ silently ignoredCtrl+C\x03→CTRL_Cevent → cancels runTesting
tests/ui_and_conv/test_keyboard_raw_mode.pycovering:ISIG,IEXTEN,ICANON,ECHOall cleared)KeyEventmappings (parametrized)CTRL_Cdispatches cancel event in_LiveViewESCAPEstill dispatches cancel (regression test)CTRL_Cis a no-op when no cancel event is setChecklist
make gen-changelogto update the changelog.