From 5ee204bb3f4a1a09b6d3fef00d26ca05a8acbecc Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 7 Sep 2026 15:20:31 -0700 Subject: [PATCH] perf(terminal): reuse forward OSC status terminator searches --- config/reliability-gates.jsonc | 81 ++++++++++++++++++ .../agent-status-osc-scan-budget.test.ts | 84 +++++++++++++++++++ src/shared/agent-status-osc.test.ts | 67 +++++++++++++++ src/shared/agent-status-osc.ts | 26 +++--- 4 files changed, 247 insertions(+), 11 deletions(-) create mode 100644 src/shared/agent-status-osc-scan-budget.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 8a1349a7852..8ca75be382e 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -83,6 +83,87 @@ ], "demotionRule": "Keep experimental until CI soak; investigate fidelity or count failures without relaxing the row budget." }, + { + "id": "terminal-performance.osc-status-scan-budget", + "title": "OSC 9999 status bursts reuse forward terminator searches", + "maturity": "experimental", + "protection": "partial", + "owner": "terminal-runtime", + "layer": "shared-unit-and-runtime-unit", + "surfaces": ["terminal output ingestion", "terminal agent-status side effects"], + "platforms": ["macos", "linux", "windows"], + "providers": ["local", "daemon", "ssh", "remote-runtime"], + "coveredPlatforms": ["macos"], + "coveredProviders": ["local", "daemon", "ssh", "remote-runtime"], + "coverageNotes": "Shared parser tests cover provider-independent bytes; main and renderer contract tests cover status and terminal-output delivery. Live Linux, Windows, WSL, SSH and remote-runtime processes are not launched. Execution, liveness, paths, wire formats and mobile UI are unchanged.", + "motivatingLinks": [ + "https://github.com/stablyai/orca/blob/main/src/shared/agent-status-osc.ts" + ], + "invariant": "Terminal status parsing preserves ordinary UTF-16 output, every valid payload in order, the last valid payload's clean-output offset, earliest BEL/ST termination, and incomplete-frame caps while searching each complete burst only forward.", + "oracle": "Two 5,000-frame bursts using exclusively BEL or ST produce every expected payload and ordinary output byte with at most twice the input length in native search ranges. Mixed terminators, every split through prefixes/JSON/ST, independent parser interleaving, malformed payloads, exact pending-cap boundaries and oversized complete frames retain their previous behavior. A one-character echo performs no terminator search. Parsed output chunks are not retained in legacy regular-expression state.", + "commands": [ + "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/shared/agent-status-osc.test.ts src/shared/agent-status-osc-scan-budget.test.ts src/shared/agent-status-types.test.ts src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts src/renderer/src/components/terminal-pane/terminal-title-tracker-parity.test.ts src/renderer/src/components/terminal-pane/pty-connection-main-side-effect-authority.test.ts src/renderer/src/components/terminal-pane/pty-connection-hook-completion-side-effects.test.ts src/renderer/src/components/terminal-pane/pty-transport-eager-buffer-replay.test.ts" + ], + "testFiles": [ + "src/shared/agent-status-osc.test.ts", + "src/shared/agent-status-osc-scan-budget.test.ts", + "src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts", + "src/renderer/src/components/terminal-pane/terminal-title-tracker-parity.test.ts" + ], + "assertionRefs": [ + { + "file": "src/shared/agent-status-osc-scan-budget.test.ts", + "assertions": [ + "reads each burst only forward with terminator %j", + "keeps a one-character input echo on the ordinary-output path", + "does not retain the output chunk in legacy regular-expression state" + ] + }, + { + "file": "src/shared/agent-status-osc.test.ts", + "assertions": [ + "uses the earliest mixed terminator and counts only parsed payload offsets", + "preserves every split of prefixes, JSON, and both terminators across independent streams", + "applies the pending cap only to incomplete frames" + ] + } + ], + "evidenceRuns": [ + { + "date": "2026-09-07", + "runner": "local", + "platform": "macos", + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/shared/agent-status-osc.test.ts src/shared/agent-status-osc-scan-budget.test.ts src/shared/agent-status-types.test.ts src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts src/renderer/src/components/terminal-pane/terminal-title-tracker-parity.test.ts src/renderer/src/components/terminal-pane/pty-connection-main-side-effect-authority.test.ts src/renderer/src/components/terminal-pane/pty-connection-hook-completion-side-effects.test.ts src/renderer/src/components/terminal-pane/pty-transport-eager-buffer-replay.test.ts", + "result": "passed", + "durationSeconds": 23.96, + "summary": "153 tests passed across eight files. Independent baseline differential review also matched 3,704 streams and 45,141 chunk results." + } + ], + "runtimeBudget": { + "p95Seconds": 30, + "scope": "Shared parser and main/renderer terminal contract tests; no launched app." + }, + "flakeHistory": { + "status": "not-started", + "evidence": "Initial deterministic local validation; CI soak has not started." + }, + "redGreenEvidence": { + "status": "complete", + "evidence": "The unchanged parser failed both search budgets: 618,560,785 searched characters for the 246,390-character BEL burst and 631,068,285 for the 251,390-character ST burst. Reusing forward match positions reduces those totals to 492,770 and 502,770 characters respectively, within twice the input length, with identical complete results." + }, + "performanceBudget": { + "required": true, + "evidence": "Warmed Node 24 macOS CPU medians: a 250 KB / 5,000-status burst fell from 100.240 ms to 1.903 ms; a 1 MB / 20,000-status burst fell from 1,588.492 ms to 7.369 ms. Wall-clock medians were 134.878 to 2.484 ms and 2,536.927 to 12.902 ms under concurrent machine load. These are adverse bursts, not typical callback sizes. The ordinary-output path is unchanged; one-character echo CPU was 2.173 versus 2.342 ms per 100,000 calls, and single-status BEL CPU was 10.835 versus 10.897 ms per 30,000 calls. Both native terminator searches advance monotonically within the current chunk; no regex state retains the input. No scheduling, polling, provider calls, pending limits, output filtering or payload parsing changed." + }, + "knownGaps": [ + "Live Electron input latency and Linux/Windows/WSL/SSH execution have not been measured for this parser-only change.", + "Fragmented unterminated payload accumulation and downstream processing of large status arrays remain outside this complete-burst search budget." + ], + "promotionCriteria": [ + "Complete CI soak while preserving byte fidelity and deterministic search budgets." + ], + "demotionRule": "Keep experimental until CI soak; investigate output, offset, carry or search-budget failures without relaxing the oracle." + }, { "id": "terminal-performance.padded-fullscreen-redraw", "title": "Fullscreen redraw padding does not stall terminal delivery", diff --git a/src/shared/agent-status-osc-scan-budget.test.ts b/src/shared/agent-status-osc-scan-budget.test.ts new file mode 100644 index 00000000000..c61e2faded1 --- /dev/null +++ b/src/shared/agent-status-osc-scan-budget.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it, vi } from 'vitest' +import { createAgentStatusOscProcessor } from './agent-status-osc' + +function parseWithSearchBudget(data: string) { + let searchedChars = 0 + const indexOf = String.prototype.indexOf + const charCodeAt = String.prototype.charCodeAt + const exec = RegExp.prototype.exec + const indexOfSpy = vi.spyOn(String.prototype, 'indexOf').mockImplementation(function ( + this: string, + search, + from = 0 + ) { + const found = indexOf.call(this, search, from) + if (this === data) { + searchedChars += (found === -1 ? data.length : found + search.length) - from + } + return found + }) + const execSpy = vi + .spyOn(RegExp.prototype, 'exec') + .mockImplementation(function (this: RegExp, input) { + const from = this.global || this.sticky ? this.lastIndex : 0 + const found = exec.call(this, input) + if (input === data) { + searchedChars += (found === null ? data.length : found.index + found[0].length) - from + } + return found + }) + const charCodeAtSpy = vi + .spyOn(String.prototype, 'charCodeAt') + .mockImplementation(function (this: string, index) { + if (this === data) { + searchedChars += 1 + } + return charCodeAt.call(this, index) + }) + try { + return { result: createAgentStatusOscProcessor()(data), searchedChars } + } finally { + indexOfSpy.mockRestore() + execSpy.mockRestore() + charCodeAtSpy.mockRestore() + } +} + +describe('OSC 9999 scan budget', () => { + it.each(['\x07', '\x1b\\'])('reads each burst only forward with terminator %j', (terminator) => { + const count = 5000 + const statuses = Array.from({ length: count }, (_, index) => ({ + state: index % 2 === 0 ? 'working' : 'done', + prompt: `turn ${index}` + })) + const data = statuses + .map((status) => `log\x1b]9999;${JSON.stringify(status)}${terminator}`) + .join('') + + const { result, searchedChars } = parseWithSearchBudget(data) + + expect(result).toEqual({ + cleanData: 'log'.repeat(count), + payloads: statuses, + lastPayloadCleanOffset: count * 3 + }) + expect(searchedChars).toBeLessThanOrEqual(data.length * 2) + }) + + it('keeps a one-character input echo on the ordinary-output path', () => { + const { result, searchedChars } = parseWithSearchBudget('a') + + expect(result).toEqual({ cleanData: 'a', payloads: [], lastPayloadCleanOffset: null }) + expect(searchedChars).toBe(1) + }) + + it('does not retain the output chunk in legacy regular-expression state', () => { + const data = `\x1b]9999;{"state":"working"}\x07${'output'.repeat(200_000)}` + void /reset/.test('reset') + + createAgentStatusOscProcessor()(data) + const retainedInput = RegExp.input + + expect(retainedInput).not.toBe(data) + }) +}) diff --git a/src/shared/agent-status-osc.test.ts b/src/shared/agent-status-osc.test.ts index 5b7c0ff69d1..dc143cedae7 100644 --- a/src/shared/agent-status-osc.test.ts +++ b/src/shared/agent-status-osc.test.ts @@ -90,4 +90,71 @@ describe('createAgentStatusOscProcessor', () => { expect(result.payloads).toEqual([]) expect(result.lastPayloadCleanOffset).toBeNull() }) + + it('uses the earliest mixed terminator and counts only parsed payload offsets', () => { + const process = createAgentStatusOscProcessor() + const result = process( + '😀\x1b]9999;{"state":"working"}\x07A\x1b\\' + + '\x1b]9999;{"state":"done"}\x1b\\B\x07' + + '\x1b]9999;{malformed}\x07C' + ) + + expect(result).toEqual({ + cleanData: '😀A\x1b\\B\x07C', + payloads: [ + { state: 'working', prompt: '' }, + { state: 'done', prompt: '' } + ], + lastPayloadCleanOffset: '😀A\x1b\\'.length + }) + }) + + it('preserves every split of prefixes, JSON, and both terminators across independent streams', () => { + const stream = + 'before😀\x1b]9999;{"state":"working","prompt":"漢字"}\x07between' + + '\x1b]9999;{"state":"done"}\x1b\\after' + const expected = createAgentStatusOscProcessor()(stream) + const other = createAgentStatusOscProcessor() + + for (let split = 0; split <= stream.length; split += 1) { + const process = createAgentStatusOscProcessor() + const first = process(stream.slice(0, split)) + expect(other('\x1b]9999;{"state":"blocked"}\x07').payloads).toEqual([ + { state: 'blocked', prompt: '' } + ]) + const second = process(stream.slice(split)) + + expect(first.cleanData + second.cleanData, `split ${split}`).toBe(expected.cleanData) + expect([...first.payloads, ...second.payloads], `split ${split}`).toEqual(expected.payloads) + const lastOffset = + second.lastPayloadCleanOffset === null + ? first.lastPayloadCleanOffset + : first.cleanData.length + second.lastPayloadCleanOffset + expect(lastOffset, `split ${split}`).toBe(expected.lastPayloadCleanOffset) + } + }) + + it('applies the pending cap only to incomplete frames', () => { + const marker = '\x1b]9999;{"state":"working"}' + const atCap = marker + ' '.repeat(64 * 1024 - marker.length) + const retained = createAgentStatusOscProcessor() + expect(retained(`before${atCap}`).cleanData).toBe('before') + expect(retained('\x1b')).toEqual({ cleanData: '', payloads: [], lastPayloadCleanOffset: null }) + expect(retained('\\after').cleanData).toBe('\\after') + + const exact = createAgentStatusOscProcessor() + exact(atCap) + expect(exact('\x07after')).toEqual({ + cleanData: 'after', + payloads: [{ state: 'working', prompt: '' }], + lastPayloadCleanOffset: 0 + }) + + const complete = createAgentStatusOscProcessor() + expect(complete(`${atCap} \x1b\\after`)).toEqual({ + cleanData: 'after', + payloads: [{ state: 'working', prompt: '' }], + lastPayloadCleanOffset: 0 + }) + }) }) diff --git a/src/shared/agent-status-osc.ts b/src/shared/agent-status-osc.ts index 75036888dbc..fdb183f442c 100644 --- a/src/shared/agent-status-osc.ts +++ b/src/shared/agent-status-osc.ts @@ -27,20 +27,23 @@ export type ProcessedAgentStatusChunk = { function findAgentStatusTerminator( data: string, - searchFrom: number + searchFrom: number, + next: { belIndex: number; stIndex: number } ): { index: number; length: 1 | 2 } | null { - const belIndex = data.indexOf('\x07', searchFrom) - const stIndex = data.indexOf('\x1b\\', searchFrom) - if (belIndex === -1 && stIndex === -1) { + // Reuse forward matches, including absence, for this chunk's remaining frames. + if (next.belIndex !== -1 && next.belIndex < searchFrom) { + next.belIndex = data.indexOf('\x07', searchFrom) + } + if (next.stIndex !== -1 && next.stIndex < searchFrom) { + next.stIndex = data.indexOf('\x1b\\', searchFrom) + } + if (next.belIndex === -1 && next.stIndex === -1) { return null } - if (belIndex === -1) { - return { index: stIndex, length: 2 } + if (next.stIndex === -1 || (next.belIndex !== -1 && next.belIndex < next.stIndex)) { + return { index: next.belIndex, length: 1 } } - if (stIndex === -1 || belIndex < stIndex) { - return { index: belIndex, length: 1 } - } - return { index: stIndex, length: 2 } + return { index: next.stIndex, length: 2 } } /** @@ -76,6 +79,7 @@ export function createAgentStatusOscProcessor(): (data: string) => ProcessedAgen let lastPayloadCleanOffset: number | null = null let cleanData = '' let cursor = 0 + const nextTerminator = { belIndex: 0, stIndex: 0 } while (cursor < combined.length) { const start = combined.indexOf(OSC_AGENT_STATUS_PREFIX, cursor) @@ -93,7 +97,7 @@ export function createAgentStatusOscProcessor(): (data: string) => ProcessedAgen cleanData += combined.slice(cursor, start) const payloadStart = start + OSC_AGENT_STATUS_PREFIX.length - const terminator = findAgentStatusTerminator(combined, payloadStart) + const terminator = findAgentStatusTerminator(combined, payloadStart, nextTerminator) if (terminator === null) { const candidate = combined.slice(start)