From 4e08a28cdf8ec99add0693e713da9335a6e7d201 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Thu, 2 Jul 2026 22:54:42 -0400 Subject: [PATCH] Window the retained-tail redraw path to the cursor's reach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attribution (findings log 2026-07-03): main's onPtyData consumed ~93% of the event loop under an agent-TUI flood, and the dominant term was appendNormalizedToMultilineTailBuffer + finalizeRetainedTerminalRows materializing ~2x tail-length row objects plus a per-row trailing-space regex on every chunk — 0.888ms/chunk at the 2,000-line cap, on every Claude-Code-shaped frame (cursor-up + erase-below). The multiline algorithm now runs on a suffix window sized by the chunk's maximum upward cursor excursion (plus the inherited redraw cursor and a safety margin); the untouched prefix is shared by reference with a cheap last-char trailing-space check to match the reference trim. Pathological full-height cursor-ups fall back to the unwindowed implementation, which is kept verbatim and exported as the reference for the 500-case differential fuzz (retained-tail-redraw-window.equivalence.test.ts). Micro-bench at a full 2,000-line tail: 0.888 -> 0.073 ms/chunk (12x). 1,415 runtime tests green, typecheck clean. Co-authored-by: Orca --- src/main/runtime/orca-runtime.ts | 97 +++++++++++++++++++ ...ned-tail-redraw-window.equivalence.test.ts | 90 +++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/main/runtime/retained-tail-redraw-window.equivalence.test.ts diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index 79dbbdea492..f71f26b1e63 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -22277,6 +22277,30 @@ function trimTerminalLineRight(line: string): string { return end === line.length ? line : line.slice(0, end) } +// Why a window: the unwindowed implementation below materializes a row object +// per retained tail line and finalize re-allocates + regex-trims every row — +// O(tail) per chunk (~0.9ms at the 2,000-line cap), measured at ~93% of the +// main-process event loop under an agent-TUI flood (findings log 2026-07-03). +// A redraw can only touch rows the cursor can reach, so run the algorithm on +// a suffix window sized by the chunk's maximum upward cursor excursion and +// share the untouched prefix by reference. Equality with the unwindowed +// implementation is fuzz-verified in +// retained-tail-redraw-window.equivalence.test.ts. +const REDRAW_WINDOW_SAFETY_ROWS = 8 + +function maxUpwardCursorReach( + normalizedChunk: string, + previousRedrawCursor: RetainedTailRedrawCursor | null +): number { + let reach = previousRedrawCursor ? previousRedrawCursor.rowFromEnd : 0 + const cursorUpPattern = /\x1b\[(\d*)(?:;[\d;]*)?A/g + let match: RegExpExecArray | null + while ((match = cursorUpPattern.exec(normalizedChunk)) !== null) { + reach += match[1] ? Number.parseInt(match[1], 10) : 1 + } + return reach +} + function appendNormalizedToMultilineTailBuffer( previousLines: string[], boundedPreviousPartialLine: string, @@ -22289,6 +22313,79 @@ function appendNormalizedToMultilineTailBuffer( redrawCursor: RetainedTailRedrawCursor | null truncated: boolean newCompleteLines: number +} { + const windowRows = + maxUpwardCursorReach(normalizedChunk, previousRedrawCursor) + REDRAW_WINDOW_SAFETY_ROWS + if (windowRows >= previousLines.length) { + return appendNormalizedToMultilineTailBufferUnwindowed( + previousLines, + boundedPreviousPartialLine, + normalizedChunk, + previousPartialWasCapped, + previousRedrawCursor + ) + } + const prefixLength = previousLines.length - windowRows + const suffix = previousLines.slice(prefixLength) + const windowed = appendNormalizedToMultilineTailBufferUnwindowed( + suffix, + boundedPreviousPartialLine, + normalizedChunk, + previousPartialWasCapped, + previousRedrawCursor + ) + let lines = previousLines.slice(0, prefixLength) + // Why: the unwindowed finalize trims trailing spaces/tabs on every row; the + // shared prefix must match without paying a regex per untouched row. + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index]! + const lastChar = line.charCodeAt(line.length - 1) + if (lastChar === 32 || lastChar === 9) { + lines[index] = line.replace(/[ \t]+$/g, '') + } + } + for (const line of windowed.lines) { + lines.push(line) + } + let truncated = windowed.truncated + if (lines.length > MAX_TAIL_LINES) { + lines = lines.slice(lines.length - MAX_TAIL_LINES) + truncated = true + } + let totalChars = windowed.partialLine.length + for (const line of lines) { + totalChars += line.length + } + let dropCount = 0 + while (dropCount < lines.length && totalChars > MAX_TAIL_CHARS) { + totalChars -= lines[dropCount]!.length + dropCount += 1 + } + if (dropCount > 0) { + lines = lines.slice(dropCount) + truncated = true + } + return { + lines, + partialLine: windowed.partialLine, + redrawCursor: windowed.redrawCursor, + truncated, + newCompleteLines: windowed.newCompleteLines + } +} + +export function appendNormalizedToMultilineTailBufferUnwindowed( + previousLines: string[], + boundedPreviousPartialLine: string, + normalizedChunk: string, + previousPartialWasCapped: boolean, + previousRedrawCursor: RetainedTailRedrawCursor | null +): { + lines: string[] + partialLine: string + redrawCursor: RetainedTailRedrawCursor | null + truncated: boolean + newCompleteLines: number } { const rows: RetainedTerminalRow[] = [ ...previousLines.map((line) => ({ text: line, completed: true })), diff --git a/src/main/runtime/retained-tail-redraw-window.equivalence.test.ts b/src/main/runtime/retained-tail-redraw-window.equivalence.test.ts new file mode 100644 index 00000000000..32685f93b53 --- /dev/null +++ b/src/main/runtime/retained-tail-redraw-window.equivalence.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it } from 'vitest' +import { + appendNormalizedToTailBuffer, + appendNormalizedToMultilineTailBufferUnwindowed +} from './orca-runtime' + +// Differential guard for the windowed redraw tail path: the public +// appendNormalizedToTailBuffer routes vertical-control chunks through a +// suffix-windowed wrapper (findings log 2026-07-03 — the unwindowed path was +// O(tail) per chunk and dominated main's event loop under agent-TUI floods). +// This fuzz asserts the windowed result is byte-identical to the reference +// implementation across randomized tails and redraw chunks. + +function mulberry32(seed: number): () => number { + let a = seed >>> 0 + return () => { + a = (a + 0x6d2b79f5) | 0 + let t = Math.imul(a ^ (a >>> 15), 1 | a) + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t + return ((t ^ (t >>> 14)) >>> 0) / 4294967296 + } +} + +function randomTail(rng: () => number, maxLines: number): string[] { + const count = Math.floor(rng() * maxLines) + return Array.from({ length: count }, (_, i) => { + const base = `line ${i} ${'x'.repeat(Math.floor(rng() * 40))}` + // Trailing whitespace included deliberately: the reference implementation + // trims every row on each call, so the windowed prefix must match. + return rng() < 0.3 ? `${base} ` : base + }) +} + +function randomRedrawChunk(rng: () => number): string { + const parts: string[] = [] + const ops = 1 + Math.floor(rng() * 12) + for (let i = 0; i < ops; i++) { + const roll = rng() + if (roll < 0.2) { + parts.push(`\x1b[${1 + Math.floor(rng() * 12)}A`) + } else if (roll < 0.3) { + parts.push(`\x1b[${Math.floor(rng() * 3)}J`) + } else if (roll < 0.4) { + parts.push(`\x1b[${Math.floor(rng() * 3)}K`) + } else if (roll < 0.5) { + parts.push('\r') + } else if (roll < 0.6) { + parts.push(`\x1b[${1 + Math.floor(rng() * 30)}G`) + } else if (roll < 0.7) { + parts.push('\n') + } else if (roll < 0.75) { + parts.push('') + } else { + parts.push(`text${Math.floor(rng() * 100)} ${'y'.repeat(Math.floor(rng() * 20))}`) + } + } + return parts.join('') +} + +describe('windowed redraw tail equivalence', () => { + it('matches the unwindowed reference across 500 randomized cases', () => { + const rng = mulberry32(42) + for (let round = 0; round < 500; round++) { + const tail = randomTail(rng, round % 5 === 0 ? 2100 : 300) + const partial = rng() < 0.5 ? `partial ${'z'.repeat(Math.floor(rng() * 30))}` : '' + const redrawCursor = + rng() < 0.3 ? { rowFromEnd: Math.floor(rng() * 20), column: Math.floor(rng() * 40) } : null + // Why the guaranteed cursor-up: the public function routes to the + // multiline (windowed) path only for vertical-control chunks; chunks + // without one take the single-line fast path, which is out of scope. + const chunk = `\x1b[${1 + Math.floor(rng() * 4)}A${randomRedrawChunk(rng)}` + + const actual = appendNormalizedToTailBuffer(tail, partial, chunk, redrawCursor) + // Reference path over the full tail. + const expected = appendNormalizedToMultilineTailBufferUnwindowed( + tail, + partial.slice(-4000), + chunk, + partial.length > 4000, + redrawCursor + ) + + expect(actual.lines, `round ${round} lines`).toEqual(expected.lines) + expect(actual.partialLine, `round ${round} partial`).toBe(expected.partialLine) + expect(actual.redrawCursor, `round ${round} cursor`).toEqual(expected.redrawCursor) + expect(actual.truncated, `round ${round} truncated`).toBe(expected.truncated) + expect(actual.newCompleteLines, `round ${round} newLines`).toBe(expected.newCompleteLines) + } + }) +})