Skip to content

fix(pty): strip terminal control sequences from pty_read and pty_exited output - #79

Open
NAnD71 wants to merge 2 commits into
shekohex:mainfrom
NAnD71:fix/strip-terminal-control-output
Open

NAnD71 wants to merge 2 commits into
shekohex:mainfrom
NAnD71:fix/strip-terminal-control-output

Conversation

@NAnD71

@NAnD71 NAnD71 commented Oct 10, 2026 •

Copy link
Copy Markdown

Summary

pty_read results and <pty_exited> notifications pass raw terminal output to the host. Escape sequences in that output are useless to the agent, and the OpenCode TUI executes them because it writes text content to the terminal verbatim (anomalyco/opencode#47687). This affects both V1 and V2. On Windows it hits almost every session, because ConPTY prefixes output with mode, clear-screen and window-title sequences.

Fixes #75. Supersedes #76.

Changes

  • src/plugin/pty/sanitize.ts: sanitizeTerminalText(), built on node:util stripVTControlCharacters, plus removal of OSC/DCS/APC strings and leftover C0/C1 control characters. Tabs are kept. Trailing whitespace is dropped, because ConPTY renders an erased line as text padded with spaces to the terminal width. Lines without control characters skip the regex passes. No Bun-only APIs, so it also works under V2's Node build.
  • pty_read: lines are sanitized before formatting. Pattern search matches against the sanitized text and returns it.
  • <pty_exited>: Last Line is sanitized, and lines that are empty after sanitizing are skipped. The buffer is now split once instead of once per scanned line.
  • The raw buffer, the Web UI and /api/sessions/:id/buffer/raw are unchanged.
  • PTY_SANITIZE_OUTPUT=0 / false turns this off and keeps the raw output (documented in the README).

Testing

  • test/pty-output-sanitize.test.ts (end to end): spawns a real PTY child (the current Bun binary, so it runs on Linux and Windows) that writes colours, a window title, line erases and \r. It then checks pty_read, pattern search and the exit notification for control characters, and checks that PTY_SANITIZE_OUTPUT=0 returns raw output. On Windows, this also covers the ConPTY preamble and padding.
  • Unit tests in test/sanitize.test.ts, pty-tools.test.ts and notification-manager.test.ts cover the ConPTY preamble, OSC/DCS/APC, \r, padding, search on sanitized text, and the env switch.
  • bun lint, bun typecheck: pass.
  • Performance (Bun 1.4.2, 1 MB buffer = the default max size):
    • Default pty_read (500 lines): ~0.1 ms for plain output, ~0.7 ms for coloured output.
    • Pattern search over the full buffer: 0.6 → 3.8 ms for plain output, 0.5 → 17.6 ms for coloured output.
    • Pathological single lines (unterminated OSC/CSI, 300k chars of bare ESC[1) stay linear, worst ~20 ms.
  • On my Windows machine, the existing tests that spawn echo/sleep/bash fail with PTY spawn failed (those binaries don't exist there). The failing set is identical on main.

…ed output

PTY output is handed to the host as plain text: pty_read results and the <pty_exited> notification. Escape sequences in it (on Windows, ConPTY prefixes every session with mode, clear-screen and window-title sequences) carry no information for the agent and are executed by the OpenCode TUI, which writes text content to the terminal verbatim in both V1 and V2.

Strip them with node:util stripVTControlCharacters plus string-type sequences (OSC/DCS/APC) and leftover C0/C1 control characters, so it works under both Bun and Node hosts. Search now matches against the sanitized text, and the exit notification skips lines that are empty once sanitized. The raw buffer used by the Web UI is unchanged. PTY_SANITIZE_OUTPUT=0 restores the raw output.

Fixes shekohex#75
…ding

Add a test that runs a real PTY child and checks pty_read, pattern search and the exit notification, plus the PTY_SANITIZE_OUTPUT=0 escape hatch. The child is the current Bun binary so the test also runs on Windows, where ConPTY adds its own preamble.

The test showed that ConPTY renders an erased line as text padded with spaces to the terminal width, so sanitized lines now drop trailing whitespace. Lines without control characters skip the regex passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PTY output pollutes OpenCode session with ANSI escape sequences, breaking keyboard input on resume

1 participant