Repository navigation
Conversation
…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.
|
While playing around this myself ( https://gist.github.com/BananaAcid/e0374ff83a2d774d4cf8d76d54ea10da ) I realized, I needed to see the escape sequences as normal text, but just not have them applied to not ruin the TUI. and pty_read should get the raw to be able to have the LLM understand it (like understanding by colored logs or debugging a TUI application). |
You're right,I realized escaping is the better default than stripped. The LLM keeps the color/formatting context, and the TUI stays safe. The change should be straightforward: same sanitize path, just swap the strip call for an escape function (e.g. ESC → ␛, CSI/OSC shown as readable text). Raw buffer and Web UI stay untouched. I won't have time to get to this soon,so if you're up for it,it's okay to push directly or open a PR against my fork. |
Summary
pty_readresults 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 onnode:utilstripVTControlCharacters, 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 Lineis sanitized, and lines that are empty after sanitizing are skipped. The buffer is now split once instead of once per scanned line./api/sessions/:id/buffer/raware unchanged.PTY_SANITIZE_OUTPUT=0/falseturns 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 checkspty_read, pattern search and the exit notification for control characters, and checks thatPTY_SANITIZE_OUTPUT=0returns raw output. On Windows, this also covers the ConPTY preamble and padding.test/sanitize.test.ts,pty-tools.test.tsandnotification-manager.test.tscover the ConPTY preamble, OSC/DCS/APC,\r, padding, search on sanitized text, and the env switch.bun lint,bun typecheck: pass.pty_read(500 lines): ~0.1 ms for plain output, ~0.7 ms for coloured output.ESC[1) stay linear, worst ~20 ms.echo/sleep/bashfail withPTY spawn failed(those binaries don't exist there). The failing set is identical onmain.