MoonshotAI / MoonshotAI/kimi-code
Terminal left in Kitty keyboard protocol after abnormal exit (SSH disconnect / kill -9) — keystrokes emit garbage like `13;129u`
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
What version of Kimi Code is running?
kimi-code 0.28.0 (from kimi --version)
Which open platform/subscription were you using?
Not relevant to this bug — it reproduces before any login/model selection, during the TUI's initial terminal-mode setup. (If a value is mandatory for the form: "Kimi-K3 thinking", but the bug is independent of platform/model.)
Which model were you using?
N/A — bug triggers at TUI startup, before any model interaction.
What platform is your computer?
Linux x86_64 6.5.0-44-generic GNU/Linux (reproduced under a pty; the user-facing occurrence was on a remote Linux host reached via SSH from a Ghostty client on macOS). The bug is platform-independent: it is a terminal-protocol issue, not an OS-specific one.
What issue are you seeing?
When the kimi-code TUI is terminated abnormally (network drop on an SSH session, kill -9, crash, terminal closed without an orderly exit), the Kitty keyboard protocol it enables at startup is never disabled. The client terminal is left with the protocol still active, so every subsequent keystroke in the local shell is encoded as a CSI-u sequence and printed as literal characters instead of performing its action.
Concrete symptoms reported by the user (quoting their description, translated):
After an SSH disconnect while running kimi-code on the remote host, returning to my local shell, keyboard input starts emitting extra/garbage characters.
Observed garbled output examples (typical for a terminal stuck in Kitty keyboard protocol flags 7):
- Enter prints
13;129uinstead of executing the line - Ctrl+C prints
9;5uinstead of sending SIGINT - Arrow keys print
1;129A/1;129B/ … instead of moving the cursor - Mouse clicks (if focus/mouse reporting was also left on) print
0;38;15M-style coordinates
What steps can reproduce the bug?
The SSH-disconnect case is inherently hard to script, but kill -9 is its precise equivalent: the process gets no chance to run cleanup code, which is exactly what happens on a broken pipe. I reproduced it locally under a pty with byte-level capture, which is the most faithful equivalent of an abrupt disconnect.
Setup: capture every byte the kimi TUI writes to the terminal.
# /tmp/opencode/kimi_wrap.sh
#!/bin/bash
export TERM=xterm-kitty
node ~/.local/lib/node_modules/@moonshot-ai/kimi-code/dist/main.mjs &
PID=$!
echo $PID > /tmp/opencode/kimi.pid
trap "kill $PID 2>/dev/null" EXIT
wait $PID
# Reproduce
OUT=/tmp/opencode/kimi_capture.bin
rm -f "$OUT"
script -qfc "/tmp/opencode/kimi_wrap.sh" "$OUT" >/dev/null 2>&1 &
# Wait until the TUI has pushed the Kitty keyboard protocol
for i in $(seq 1 25); do
KIMI_PID=$(cat /tmp/opencode/kimi.pid 2>/dev/null)
[ -n "$KIMI_PID" ] && kill -0 "$KIMI_PID" 2>/dev/null \
&& grep -aq $'\x1b\\[>7u' "$OUT" 2>/dev/null && break
sleep 1
done
# Simulate an SSH disconnect / crash
kill -9 "$KIMI_PID"
sleep 2
Expected bytes in $OUT after the kill: at minimum one ESC[<u (pop the Kitty keyboard protocol stack) emitted by the cleanup path.
Actual bytes:
| Sequence | Meaning | Count in capture |
|---|---|---|
ESC[>7u |
push Kitty keyboard protocol flags 7 (disambiguate + report-events + report-alternates) | 1 |
ESC[<u |
pop Kitty keyboard protocol stack | 0 |
ESC[?u |
query current flags | 1 |
ESC[c |
DA sentinel for protocol response boundary | 1 |
ESC[?2004h |
enable bracketed paste | 1 |
ESC[?1004h |
enable focus reporting | 1 |
Full startup sequence captured (in order):
ESC[>7u ← push kitty keyboard protocol flags 7
ESC[?u ← query current flags
ESC[c ← DA sentinel
ESC[?2004h ← bracketed paste on
ESC[?1004h ← focus reporting on
After kill -9, the capture contains no ESC[<u, no ESC[>4;Nm (modifyOtherKeys reset — note: kimi-code does not appear to enable modifyOtherKeys at all, so that one is expected to be absent), and no disables for ?2004 / ?1004. The terminal is left with all of these modes still active.
This is byte-for-byte the state an SSH client leaves the local terminal in after the remote kimi-code process dies on network drop.
Quick verification (no SSH needed): on any terminal advertising Kitty keyboard protocol support, run kimi in the foreground, then from another terminal send kill -9 $(pgrep -f kimi-code/dist/main.mjs). Typing in the now-returned local shell will produce …u sequences instead of normal input. Run printf '\e[<u' && stty sane && reset to recover.
What is the expected behavior?
On any termination path where kimi-code has the opportunity to execute cleanup code, it should restore the terminal to the state it found it in. At minimum, before exit, emit:
ESC[<u ← pop the Kitty keyboard protocol stack
ESC[?2004l ← disable bracketed paste
ESC[?1004l ← disable focus reporting
ESC[?1006l ← disable SGR mouse mode (if enabled)
ESC[?25h ← show cursor
ESC[<u is the critical one — it is the only sequence that stops the "keystrokes become garbage" symptom. The others address related mode leaks (mouse coordinates printed on click, etc.).
Acknowledged scope: this cannot fix the kill -9 / network-drop case itself — by definition the process has no chance to run cleanup. The fix targets the cases that do have a cleanup window:
- Normal exit via
/exit, Ctrl+C, Ctrl+D,q - SIGTERM / SIGSIGHUP from the parent shell
- orderly session teardown on SSH session close (TCP FIN, not RST)
It is worth auditing whether kimi-code's current exit handlers emit ESC[<u. The reproduction above shows that no ESC[<u is emitted at all on the kill path, but I have not verified the normal-exit path; this issue asks the maintainers to confirm that path is covered (and add it if not).
Additional information
Why this is distinct from #704
#704 is a Kitty-protocol negotiation bug in @earendil-works/pi-tui@0.74.2 (wrong push/query ordering causes Backspace to delete twice). This issue is about the teardown path: the protocol is correctly pushed (ESC[>7u), but never popped on abnormal exit, leaving the client terminal in a broken-input state even after kimi-code is long gone. The two bugs share the same protocol but are in different lifecycle phases.
Why this matters for SSH users specifically
TUI apps that run over SSH and enable enhanced keyboard/mouse protocols are the worst case for this class of bug, because:
- The client terminal keeps the modes active across the SSH session boundary (the modes are local to the terminal emulator, not the remote shell).
- Network drops are the most common form of abnormal exit for remote TUI usage, and they give the process zero cleanup time by definition.
- The resulting "my keyboard is broken" symptom looks to the user like a bug in their local terminal (Ghostty / Kitty / WezTerm), not in kimi-code — leading to misfiled issues elsewhere.
Related prior art
Claude Code fixed the equivalent issue in v2.1.86 (exit handlers now emit ESC[<u). OpenAI Codex has a tracked issue for the same symptom (openai/codex#16517). The fix pattern is established.
Workaround for users
Run this in the affected terminal (it works even when typed blind, with garbage echoing):
printf '\e[<u' && stty sane && reset
Or use the terminal's "Reset" action (Ghostty: right-click → Reset; Kitty: ctrl+shift+F2).
Environment details
- kimi-code: 0.28.0
- Reproduction OS: Linux x86_64 6.5.0
- Terminal used in user-facing occurrence: Ghostty (macOS client, SSH to Linux host)
- The bug is terminal-agnostic: any terminal that advertises the Kitty keyboard protocol (Ghostty, Kitty, WezTerm, foot) will exhibit the symptom. Terminals that do not support the protocol are unaffected because they ignore
ESC[>7u.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing terminal-mode setup and exit handling from dist/main.mjs, then run the provided pty reproduction and verify normal exit, SIGTERM, and SIGHUP paths. Done means cleanup emits the Kitty keyboard pop sequence and disables the terminal modes listed in the issue without affecting orderly exits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100