From f7fa06bd76a0334c954f7321be09a71a0e123464 Mon Sep 17 00:00:00 2001 From: Duy Phan Date: Fri, 14 Aug 2026 15:17:24 +0700 Subject: [PATCH] fix(tui): sanitize foreground Bash tool output Captured Bash stdout/stderr reached pi-tui unsanitized in the tool-call card, so sequences such as ESC[?1049h switched the host terminal to the alternate screen and left tmux scrollback unavailable after exit. Sanitize the accumulated buffer in ShellExecutionComponent, which covers both streaming live output and the final result. --- .changeset/sanitize-foreground-bash-output.md | 5 +++++ .../kimi-code/src/tui/components/messages/shell-execution.ts | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/sanitize-foreground-bash-output.md diff --git a/.changeset/sanitize-foreground-bash-output.md b/.changeset/sanitize-foreground-bash-output.md new file mode 100644 index 00000000000..8b3398a0fa5 --- /dev/null +++ b/.changeset/sanitize-foreground-bash-output.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Sanitize foreground Bash tool output so captured terminal escape sequences no longer change the host terminal state. diff --git a/apps/kimi-code/src/tui/components/messages/shell-execution.ts b/apps/kimi-code/src/tui/components/messages/shell-execution.ts index cb6f95dcdec..3f080f972b1 100644 --- a/apps/kimi-code/src/tui/components/messages/shell-execution.ts +++ b/apps/kimi-code/src/tui/components/messages/shell-execution.ts @@ -3,6 +3,7 @@ import { Container, Text } from '@moonshot-ai/pi-tui'; import { currentTheme } from '#/tui/theme'; import type { ToolCallBlockData, ToolResultBlockData } from '#/tui/types'; +import { sanitizeShellOutput } from '#/tui/utils/shell-output'; import type { ResultRenderer } from './tool-renderers/types'; import { PREVIEW_LINES } from './tool-renderers/types'; @@ -68,8 +69,10 @@ export class ShellExecutionComponent extends Container { expandHint: boolean, ): void { if (!result.output) return; + // Untrusted bytes: sanitize the whole buffer, not each chunk, so escape + // sequences split across live-output chunks cannot reach the terminal. this.addChild( - new TruncatedOutputComponent(result.output, { + new TruncatedOutputComponent(sanitizeShellOutput(result.output), { expanded, isError: result.is_error ?? false, maxLines: previewLines,