mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
perf(terminal): fast-path chunks without agent status markers
This commit is contained in:
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user