Bug Description
In Kitty terminal, pressing Backspace once deletes two characters instead of one. This does NOT happen in other terminal emulators or in pi-coding-agent (which uses the same TUI library).
Environment
- Terminal: Kitty 0.32.2
- kimi-code: 0.14.2 (npm global install)
- OS: Linux
- Shell: Bash
- Node: v24.16.0
Reproduction
- Open Kitty terminal
- Run
kimi
- Type some text in the input editor
- Press Backspace once
- Expected: One character deleted
- Actual: Two characters deleted
Root Cause
kimi-code ships @earendil-works/pi-tui@0.74.2, which has a buggy Kitty keyboard protocol negotiation in terminal.js. The newer pi-tui (0.79.1, used by pi-coding-agent) fixes this.
Key differences between pi-tui 0.74.2 (kimi-code) and 0.79.1 (pi-coding-agent)
1. Wrong negotiation order (most likely cause)
Old (0.74.2):
1. Send CSI ? u (query current flags)
2. Terminal responds CSI ? 0 u (flags=0, nothing pushed yet)
3. Set _kittyProtocolActive = true ← BUG: protocol not actually active yet!
4. Send CSI > 7 u (push flags 7)
5. Terminal enables flags 7
New (0.79.1):
1. Send CSI > 7 u CSI ? u CSI c (push → query → DA sentinel)
2. Terminal responds CSI ? 7 u (flags=7, already pushed)
3. Check flags !== 0 → set _kittyProtocolActive = true
The old code sets _kittyProtocolActive=true when the protocol flags are still 0 (not yet active). This creates a window where the code thinks the Kitty protocol is active but the terminal is still sending legacy-format key events (raw 0x7F for backspace). When the protocol does become active, the same keypress can be interpreted in both legacy and Kitty-protocol formats, causing double processing.
2. No negotiation buffering
The old code uses a simple regex to match the Kitty protocol response. If the response arrives split across stdin chunks, it can be missed or misinterpreted. The new code has a dedicated keyboardProtocolNegotiationBuffer with a 150ms fragment timeout.
3. No disableModifyOtherKeys()
The old code can end up with both Kitty keyboard protocol AND xterm modifyOtherKeys mode 2 active simultaneously (race condition with the 150ms fallback timeout). The new code explicitly disables modifyOtherKeys when Kitty protocol is confirmed active. Having both protocols active can cause key events to be encoded and processed twice.
4. Accepts flags=0 as "protocol active"
Old code sets _kittyProtocolActive=true on ANY Kitty response, including flags=0. New code checks flags !== 0 before enabling the protocol.
Evidence
Both apps import pi-tui as an external module (not bundled):
// kimi-code dist/main.mjs line 46:
import { ..., isKeyRelease, matchesKey, ... } from "@earendil-works/pi-tui";
Version check:
kimi-code/node_modules/@earendil-works/pi-tui: 0.74.2 ← buggy
pi-coding-agent/node_modules/@earendil-works/pi-tui: 0.79.1 ← fixed
The keys.js, stdin-buffer.js, and keybindings.js files are identical between versions — all key matching and release-event filtering logic is the same. The bug is exclusively in terminal.js protocol negotiation.
Suggested Fix
Bump the @earendil-works/pi-tui dependency from ^0.74.0 to ^0.79.0 (or latest) in apps/kimi-code/package.json.
The new version is backward-compatible — the API surface is a superset of 0.74.2 (adds MarkdownOptions, OverlayUnfocusOptions, word-navigation module, native-modifiers module). No breaking changes to the imports used by kimi-code.
Workaround
Users can manually replace the old pi-tui with a newer version:
# Replace kimi-code's pi-tui with pi-coding-agent's version
KIMI_DIR=$(npm root -g)/@moonshot-ai/kimi-code/node_modules/@earendil-works/pi-tui
PI_DIR=$(npm root -g)/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui
# Backup old version
mv "$KIMI_DIR" "${KIMI_DIR}.bak"
# Symlink to the newer version
ln -s "$PI_DIR" "$KIMI_DIR"
⚠️ This workaround will be overwritten on the next kimi-code update.
Bug Description
In Kitty terminal, pressing Backspace once deletes two characters instead of one. This does NOT happen in other terminal emulators or in pi-coding-agent (which uses the same TUI library).
Environment
Reproduction
kimiRoot Cause
kimi-code ships
@earendil-works/pi-tui@0.74.2, which has a buggy Kitty keyboard protocol negotiation interminal.js. The newer pi-tui (0.79.1, used by pi-coding-agent) fixes this.Key differences between pi-tui 0.74.2 (kimi-code) and 0.79.1 (pi-coding-agent)
1. Wrong negotiation order (most likely cause)
Old (0.74.2):
New (0.79.1):
The old code sets
_kittyProtocolActive=truewhen the protocol flags are still 0 (not yet active). This creates a window where the code thinks the Kitty protocol is active but the terminal is still sending legacy-format key events (raw0x7Ffor backspace). When the protocol does become active, the same keypress can be interpreted in both legacy and Kitty-protocol formats, causing double processing.2. No negotiation buffering
The old code uses a simple regex to match the Kitty protocol response. If the response arrives split across stdin chunks, it can be missed or misinterpreted. The new code has a dedicated
keyboardProtocolNegotiationBufferwith a 150ms fragment timeout.3. No
disableModifyOtherKeys()The old code can end up with both Kitty keyboard protocol AND xterm modifyOtherKeys mode 2 active simultaneously (race condition with the 150ms fallback timeout). The new code explicitly disables modifyOtherKeys when Kitty protocol is confirmed active. Having both protocols active can cause key events to be encoded and processed twice.
4. Accepts flags=0 as "protocol active"
Old code sets
_kittyProtocolActive=trueon ANY Kitty response, includingflags=0. New code checksflags !== 0before enabling the protocol.Evidence
Both apps import pi-tui as an external module (not bundled):
Version check:
The
keys.js,stdin-buffer.js, andkeybindings.jsfiles are identical between versions — all key matching and release-event filtering logic is the same. The bug is exclusively interminal.jsprotocol negotiation.Suggested Fix
Bump the
@earendil-works/pi-tuidependency from^0.74.0to^0.79.0(or latest) inapps/kimi-code/package.json.The new version is backward-compatible — the API surface is a superset of 0.74.2 (adds
MarkdownOptions,OverlayUnfocusOptions,word-navigationmodule,native-modifiersmodule). No breaking changes to the imports used by kimi-code.Workaround
Users can manually replace the old pi-tui with a newer version:
kimi-codeupdate.