From a0c1228824224a8257c7d8b06edcf01e6dfda254 Mon Sep 17 00:00:00 2001 From: Murat Aslan Date: Sun, 22 Mar 2026 03:27:16 +0300 Subject: [PATCH 1/2] fix(terminal): strip OSC/DA query responses from persisted history Terminal color query responses (OSC 10/11 foreground/background reports) and Device Attributes responses leak into the saved terminal history. When the session is restored, these escape sequences appear as visible gibberish like "10;rgb:f5f5/f5f5/f5f5" in the terminal. Strip these query responses from the data before persisting to history while still forwarding raw data to the live terminal emitter so xterm.js can process them correctly. Fixes #1238 --- apps/server/src/terminal/Layers/Manager.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/server/src/terminal/Layers/Manager.ts b/apps/server/src/terminal/Layers/Manager.ts index 8c71834e9..97e04d91b 100644 --- a/apps/server/src/terminal/Layers/Manager.ts +++ b/apps/server/src/terminal/Layers/Manager.ts @@ -242,6 +242,23 @@ async function defaultSubprocessChecker(terminalPid: number): Promise { return checkPosixSubprocessActivity(terminalPid); } +/** + * Strip terminal query responses (OSC color reports, DA responses) that + * should be consumed by the terminal emulator but leak into the saved + * history when the session is restored. These appear as visible gibberish + * like `10;rgb:f5f5/f5f5/f5f5` to the user. + */ +function stripTerminalQueryResponses(data: string): string { + // OSC responses: \x1b] ... ST where ST is \x1b\\ or \x07 + // Examples: \x1b]10;rgb:f5f5/f5f5/f5f5\x1b\\ (foreground color report) + // \x1b]11;rgb:1616/1616/1616\x07 (background color report) + // DA responses: \x1b[ ... c (Device Attributes) + // Example: \x1b[?1;2c + return data + .replace(/\x1b\][^\x07\x1b]*(?:\x1b\\|\x07)/g, "") + .replace(/\x1b\[\?[\d;]*c/g, ""); +} + function capHistory(history: string, maxLines: number): string { if (history.length === 0) return history; const hasTrailingNewline = history.endsWith("\n"); @@ -694,7 +711,8 @@ export class TerminalManagerRuntime extends EventEmitter } private onProcessData(session: TerminalSessionState, data: string): void { - session.history = capHistory(`${session.history}${data}`, this.historyLineLimit); + const cleanData = stripTerminalQueryResponses(data); + session.history = capHistory(`${session.history}${cleanData}`, this.historyLineLimit); session.updatedAt = new Date().toISOString(); this.queuePersist(session.threadId, session.terminalId, session.history); this.emitEvent({ From c1b2c835acab9b2fe56a57f8adee34f37329ea78 Mon Sep 17 00:00:00 2001 From: Murat Aslan Date: Sun, 22 Mar 2026 10:44:05 +0300 Subject: [PATCH 2/2] fix: also strip OSC responses when loading persisted history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handles sessions saved before this fix — any OSC/DA sequences already on disk are cleaned on load so they don't appear on restore. --- apps/server/src/terminal/Layers/Manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/terminal/Layers/Manager.ts b/apps/server/src/terminal/Layers/Manager.ts index 97e04d91b..b9164d48c 100644 --- a/apps/server/src/terminal/Layers/Manager.ts +++ b/apps/server/src/terminal/Layers/Manager.ts @@ -915,7 +915,7 @@ export class TerminalManagerRuntime extends EventEmitter const nextPath = this.historyPath(threadId, terminalId); try { const raw = await fs.promises.readFile(nextPath, "utf8"); - const capped = capHistory(raw, this.historyLineLimit); + const capped = capHistory(stripTerminalQueryResponses(raw), this.historyLineLimit); if (capped !== raw) { await fs.promises.writeFile(nextPath, capped, "utf8"); }