mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(chord): remove whole ANSI escape sequences from tool output (#11607)
`sanitizeOutput()` in `@earendil-works/chord/output` deleted the ESC byte (0x1b) along with the other control characters, but it left the rest of each escape sequence in place. Colored tool output such as `\x1b[31mERROR\x1b[0m` came back from `OutputBuffer.snapshot()` as `[31mERROR[0m`. Because the ESC byte was already gone, later ANSI stripping could no longer recognize the leftover fragments. `sanitizeOutput()` now removes whole 7-bit escape sequences before it removes single control characters. It handles: - CSI sequences (colors, cursor and erase commands) - string sequences (OSC, DCS, APC, PM, SOS) terminated by BEL or `ESC \` - short `ESC x` forms such as `ESC ( B` or `ESC 7` `snapshot()` sanitizes the concatenated retained text, so a sequence split across pushed chunks is also removed. **Testing** - Added a regression test in `packages/chord/test/output.test.ts`. It covers SGR colors, cursor and erase CSI, charset and short escapes, OSC 8 hyperlinks and an OSC title, plus an `OutputBuffer` fed a color sequence split across three chunks. The test fails without the change and passes with it. - `test/output.test.ts` and `test/output-skip.test.ts` pass. - Biome is clean. The only `tsc` errors in `npm run check` are in `packages/ai` tests and are unrelated to this change. - The durable package test results are the same with and without the change: 71 test files fail on both, and every individual test that runs passes. - Reproduction from the issue: before the change both lines printed `"[31mERROR[0m\n"`; after it, both print `"ERROR\n"`. Fixes #11604 Requested by @badlogic Co-authored-by: triagent <triagent@localhost>
This commit is contained in:
parent
102d128713
commit
50d9f823e4
2 changed files with 17 additions and 2 deletions
|
|
@ -32,13 +32,16 @@ export interface OutputSkip {
|
|||
|
||||
const NEWLINE = 0x0a;
|
||||
const INVALID_OUTPUT = /[\x00-\x08\x0b-\x1f\ufff9-\ufffb]/g;
|
||||
/** Whole 7-bit escape sequences: strings (OSC, DCS, APC, PM, SOS) up to BEL or ST, CSI, and short `ESC x` forms. */
|
||||
const ESCAPE_SEQUENCE =
|
||||
/\x1b[\]PX^_][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]|\x1b[\x20-\x2f]*[\x30-\x7e]/g;
|
||||
const encoder = new TextEncoder();
|
||||
/** Slices decode exactly: a U+FEFF at a slice's start is text, not a byte-order mark. */
|
||||
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
||||
|
||||
/** Remove control characters that break display and transcripts; tabs and newlines stay. */
|
||||
/** Remove escape sequences and control characters that break display and transcripts; tabs and newlines stay. */
|
||||
export function sanitizeOutput(text: string): string {
|
||||
return text.replace(INVALID_OUTPUT, "");
|
||||
return text.replace(ESCAPE_SEQUENCE, "").replace(INVALID_OUTPUT, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -57,6 +57,18 @@ describe("tool output bounds", () => {
|
|||
expect(sanitizeOutput("a\0b\tc\nd\re\u0007f\ufff9g\ufffbh😀")).toBe("ab\tc\ndefgh😀");
|
||||
});
|
||||
|
||||
// #11604
|
||||
it("removes whole ANSI escape sequences, not only their ESC bytes", () => {
|
||||
expect(sanitizeOutput("\x1b[31mERROR\x1b[0m\n")).toBe("ERROR\n");
|
||||
expect(sanitizeOutput("\x1b[1;38;5;208mx\x1b[?25l\x1b[2K\x1b(By\x1b7z")).toBe("xyz");
|
||||
expect(sanitizeOutput("\x1b]8;;https://pi.dev\x07link\x1b]8;;\x1b\\ \x1b]0;title\x07ok")).toBe("link ok");
|
||||
const output = new OutputBuffer({ retain: "tail", maxBytes: 1024, maxLines: 100 });
|
||||
output.push("\x1b[3");
|
||||
output.push("1mERROR\x1b[0");
|
||||
output.push("m\n");
|
||||
expect(output.snapshot().text).toBe("ERROR\n");
|
||||
});
|
||||
|
||||
it("keeps output within the limits unchanged", () => {
|
||||
expect(bound("a\nb\n", head(2))).toEqual({ kept: "a\nb\n", droppedBytes: 0, droppedLines: 0 });
|
||||
expect(bound("a\nb", tail(2))).toEqual({ kept: "a\nb", droppedBytes: 0, droppedLines: 0 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue