authorgravatar for 71498452+StiensWout@users.noreply.github.comWout Stiens <71498452+StiensWout@users.noreply.github.com> 2026-10-11 03:40:41+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-10 18:40:41-07:00
log7f0b789380f95032a55c777817cde8b49e2dae5a
tree7bfaf98e711474e9073e1b5cd7c31eb4526dd156
parentcf8479e605eb3cc8662dee38cbf1925020a1c1a7
signature Signed by PGP key B5690EEEBB952194

perf(server): skip parsing plain terminal history output (#17179)


2 files changed, 26 insertions(+), 0 deletions(-)

apps/server/src/terminal/Manager.test.ts+17
...@@ -1516,6 +1516,23 @@ it.layer(...@@ -1516,6 +1516,23 @@ it.layer(
1516 }),1516 }),
1517 );1517 );
15181518
1519 it.effect("retains plain Unicode and control characters around a split query", () =>
1520 Effect.gen(function* () {
1521 const { manager, ptyAdapter } = yield* createManager();
1522 yield* manager.open(openInput());
1523 const process = ptyAdapter.processes[0]!;
1524 const before = "café\t界🙂\u0007\u009c\u0085";
1525 const after = "e\u0301\r\n";
1526
1527 process.emitData(before);
1528 process.emitData("\u001b[5");
1529 process.emitData(`n${after}`);
1530 yield* manager.close({ threadId: "thread-1" });
1531
1532 expect((yield* manager.open(openInput())).history).toBe(before + after);
1533 }),
1534 );
1535
1519 it.effect("strips replay-unsafe terminal query and reply sequences from persisted history", () =>1536 it.effect("strips replay-unsafe terminal query and reply sequences from persisted history", () =>
1520 Effect.gen(function* () {1537 Effect.gen(function* () {
1521 const { manager, ptyAdapter } = yield* createManager();1538 const { manager, ptyAdapter } = yield* createManager();
apps/server/src/terminal/Manager.ts+9
...@@ -1115,10 +1115,19 @@ function findEscapeSequenceEndIndex(input: string, start: number): number | null...@@ -1115,10 +1115,19 @@ function findEscapeSequenceEndIndex(input: string, start: number): number | null
1115 return isEscapeFinalByte(input.charCodeAt(cursor)) ? cursor + 1 : start + 1;1115 return isEscapeFinalByte(input.charCodeAt(cursor)) ? cursor + 1 : start + 1;
1116}1116}
11171117
1118// eslint-disable-next-line no-control-regex -- Terminal sequence introducers must reach the parser.
1119const HISTORY_SEQUENCE_START = /[\u001b\u0090\u009b\u009d\u009e\u009f]/;
1120
1118function sanitizeTerminalHistoryChunk(1121function sanitizeTerminalHistoryChunk(
1119 pendingControlSequence: string,1122 pendingControlSequence: string,
1120 data: string,1123 data: string,
1121): { visibleText: string; pendingControlSequence: string } {1124): { visibleText: string; pendingControlSequence: string } {
1125 // Plain output has nothing to filter. A pending sequence still needs the parser
1126 // even when this chunk only contains its final bytes.
1127 if (pendingControlSequence.length === 0 && !HISTORY_SEQUENCE_START.test(data)) {
1128 return { visibleText: data, pendingControlSequence: "" };
1129 }
1130
1122 const input = `${pendingControlSequence}${data}`;1131 const input = `${pendingControlSequence}${data}`;
1123 let visibleText = "";1132 let visibleText = "";
1124 let index = 0;1133 let index = 0;