MoonshotAI / MoonshotAI/kimi-code

Foreground Bash tool output renders raw terminal escape sequences (breaks tmux scrollback)

Open
#2,915 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
7.5k
Forks
1.2k
Avg merge
11h 53m
Merged PRs (30d)
350

Description

Description

Foreground Bash tool stdout/stderr is rendered without sanitizing terminal
control sequences, both while output is streaming and in the final result.

If captured output contains sequences such as ESC[?1049h, pi-tui emits those
bytes to the host terminal. Inside tmux, ESC[?1049h switches the pane to the
alternate screen, making native scrollback unavailable. The pane remains in
that state after Kimi Code exits because no matching ESC[?1049l is emitted.

Reproduction

  1. Start Kimi Code inside tmux using the regular inline TUI.

  2. Ask the agent to run this exact command with the Bash tool:

    printf '\033[?1049hREPRO\n'
    

    This must be the agent-invoked Bash tool, not ! shell mode, whose output
    is already sanitized.

  3. From another pane, inspect the target pane:

    tmux display-message -p -t TARGET '#{alternate_on} #{history_size}'
    

Actual behavior

alternate_on becomes 1 (1 0 on tmux 3.7b). Native tmux scrollback is
unavailable, and the alternate-screen state persists after Kimi Code exits.

A realistic trigger is captured output from a nested TUI, for example through
script, or any command that emits raw screen-control bytes.

Expected behavior

Foreground Bash output must be treated as untrusted text and sanitized before
rendering. Rendering command output must not change the host terminal state.

Root cause

  • tool.progress stdout/stderr is forwarded to appendLiveOutput() in
    session-event-handler.ts.

  • ToolCallComponent.appendLiveOutput() accumulates the raw bytes and passes
    the complete buffer to ShellExecutionComponent.

  • Final Bash results also pass through ShellExecutionComponent.

  • ShellExecutionComponent forwards result.output to
    TruncatedOutputComponent, which applies styling and passes it to pi-tui
    Text. This path does not sanitize untrusted terminal bytes.

  • sanitizeShellOutput() already handles CSI, OSC, and other escape sequences
    for ! shell mode and background task views, but is not used here.

Suggested fix

Sanitize result.output in ShellExecutionComponent before styling and
constructing TruncatedOutputComponent. This covers both accumulated live
stdout/stderr and final Bash results.

Keep the live buffer unsanitized internally and sanitize the complete
accumulated buffer on each render. Sanitizing chunks before accumulation can
leave visible fragments when an escape sequence is split across chunks.

If sanitization is instead added to TruncatedOutputComponent, note that this
also changes rendering for generic and MCP tool results.

Regression tests

Add cases to the existing ToolCallComponent tests for:

  • live output where ESC[?1049h is split across two appendLiveOutput() calls;
  • a final Bash result containing ESC[?1049h.

Both should retain REPRO while the rendered output does not contain the
untrusted ESC[?1049h sequence.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with session-event-handler.ts, ToolCallComponent.appendLiveOutput(), and ShellExecutionComponent to trace live and final Bash output into TruncatedOutputComponent. Read the existing sanitizeShellOutput() use and ToolCallComponent tests, then add coverage for split live escape sequences and final Bash output; done means REPRO remains visible without the untrusted escape sequence in either rendering path.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.