mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 16:02:38 +00:00
Window the retained-tail redraw path to the cursor's reach
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 <help@stably.ai>
This commit is contained in:
@@ -22277,6 +22277,30 @@ function trimTerminalLineRight(line: string): string {
|
|||||||
return end === line.length ? line : line.slice(0, end)
|
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(
|
function appendNormalizedToMultilineTailBuffer(
|
||||||
previousLines: string[],
|
previousLines: string[],
|
||||||
boundedPreviousPartialLine: string,
|
boundedPreviousPartialLine: string,
|
||||||
@@ -22289,6 +22313,79 @@ function appendNormalizedToMultilineTailBuffer(
|
|||||||
redrawCursor: RetainedTailRedrawCursor | null
|
redrawCursor: RetainedTailRedrawCursor | null
|
||||||
truncated: boolean
|
truncated: boolean
|
||||||
newCompleteLines: number
|
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[] = [
|
const rows: RetainedTerminalRow[] = [
|
||||||
...previousLines.map((line) => ({ text: line, completed: true })),
|
...previousLines.map((line) => ({ text: line, completed: true })),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user