perf(terminal): fast-path chunks without agent status markers

This commit is contained in:
Neil
2026-08-30 17:42:10 -07:00
parent 3d341c132f
commit 529a12e337
2 changed files with 91 additions and 11 deletions
+55
View File
@@ -2,6 +2,17 @@ import { describe, expect, it } from 'vitest'
import { createAgentStatusOscProcessor } from './agent-status-osc'
describe('createAgentStatusOscProcessor', () => {
it('keeps ordinary chunks on the clean-data identity path', () => {
const process = createAgentStatusOscProcessor()
const data = 'plain terminal output\r\nwith ANSI-like text [0m\n'
const result = process(data)
expect(result.cleanData).toBe(data)
expect(result.payloads).toEqual([])
expect(result.lastPayloadCleanOffset).toBeNull()
})
it('strips OSC 9999 payloads from terminal data and returns parsed statuses', () => {
const process = createAgentStatusOscProcessor()
@@ -35,4 +46,48 @@ describe('createAgentStatusOscProcessor', () => {
])
expect(result.lastPayloadCleanOffset).toBe(0)
})
it('retains a marker prefix split across chunks without leaking it as output', () => {
const process = createAgentStatusOscProcessor()
const first = 'before\x1b]99'
expect(process(first)).toMatchObject({
cleanData: 'before',
payloads: [],
lastPayloadCleanOffset: null
})
const result = process('99;{"state":"working","prompt":"split"}\x07after')
expect(result.cleanData).toBe('after')
expect(result.payloads).toEqual([{ state: 'working', prompt: 'split' }])
expect(result.lastPayloadCleanOffset).toBe(0)
})
it('retains an unterminated OSC 9999 payload until its terminator arrives', () => {
const process = createAgentStatusOscProcessor()
expect(process('before\x1b]9999;{"state":"working","prompt":"par')).toMatchObject({
cleanData: 'before',
payloads: [],
lastPayloadCleanOffset: null
})
const result = process('tial"}\x1b\\after')
expect(result.cleanData).toBe('after')
expect(result.payloads).toEqual([{ state: 'working', prompt: 'partial' }])
expect(result.lastPayloadCleanOffset).toBe(0)
})
it('does not treat malformed or unrelated control data as an OSC status marker', () => {
const process = createAgentStatusOscProcessor()
const data = '\x1b[31mwarning\x07\x1b]999x\n'
const result = process(data)
expect(result.cleanData).toBe(data)
expect(result.payloads).toEqual([])
expect(result.lastPayloadCleanOffset).toBeNull()
})
})
+36 -11
View File
@@ -3,6 +3,22 @@ import { parseAgentStatusPayload } from './agent-status-types'
const OSC_AGENT_STATUS_PREFIX = '\x1b]9999;'
/** Return a suffix that can only be the beginning of an OSC 9999 marker. */
function findAgentStatusPrefixCarry(data: string): string {
const lastChar = data.charCodeAt(data.length - 1)
if (lastChar !== 0x1b && lastChar !== 0x5d && lastChar !== 0x39 && lastChar !== 0x3b) {
return ''
}
const maxCarryLength = Math.min(data.length, OSC_AGENT_STATUS_PREFIX.length - 1)
for (let length = maxCarryLength; length > 0; length -= 1) {
const suffix = data.slice(data.length - length)
if (OSC_AGENT_STATUS_PREFIX.startsWith(suffix)) {
return suffix
}
}
return ''
}
export type ProcessedAgentStatusChunk = {
cleanData: string
payloads: ParsedAgentStatusPayload[]
@@ -37,6 +53,22 @@ export function createAgentStatusOscProcessor(): (data: string) => ProcessedAgen
let pending = ''
return (data: string): ProcessedAgentStatusChunk => {
// Ordinary terminal output is by far the common case. Keep it on the
// identity path unless the chunk ends with a split OSC marker; this avoids
// rebuilding a clean-data string for every PTY frame.
if (pending.length === 0 && !data.includes(OSC_AGENT_STATUS_PREFIX)) {
const carry = findAgentStatusPrefixCarry(data)
if (carry.length === 0) {
return { cleanData: data, payloads: [], lastPayloadCleanOffset: null }
}
pending = carry
return {
cleanData: data.slice(0, data.length - carry.length),
payloads: [],
lastPayloadCleanOffset: null
}
}
const combined = pending + data
pending = ''
@@ -49,17 +81,10 @@ export function createAgentStatusOscProcessor(): (data: string) => ProcessedAgen
const start = combined.indexOf(OSC_AGENT_STATUS_PREFIX, cursor)
if (start === -1) {
const tail = combined.slice(cursor)
const prefixLen = OSC_AGENT_STATUS_PREFIX.length
let partialPrefixLen = 0
for (let k = Math.min(prefixLen - 1, tail.length); k > 0; k--) {
if (tail.endsWith(OSC_AGENT_STATUS_PREFIX.slice(0, k))) {
partialPrefixLen = k
break
}
}
if (partialPrefixLen > 0) {
cleanData += tail.slice(0, tail.length - partialPrefixLen)
pending = tail.slice(tail.length - partialPrefixLen)
const carry = findAgentStatusPrefixCarry(tail)
if (carry.length > 0) {
cleanData += tail.slice(0, tail.length - carry.length)
pending = carry
} else {
cleanData += tail
}