refactor: share terminal agent status parser (#4768)

This commit is contained in:
Neil 2026-06-07 01:10:05 -07:00 committed by GitHub
parent e180d137e9
commit 773ddccd36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 10 deletions

View File

@ -24,7 +24,10 @@ import type {
PtyDataMeta
} from './pty-dispatcher'
import { createBellDetector } from './bell-detector'
import { createAgentStatusOscProcessor, type ProcessedAgentStatusChunk } from './agent-status-osc'
import {
createAgentStatusOscProcessor,
type ProcessedAgentStatusChunk
} from '../../../../shared/agent-status-osc'
import { extractIpcErrorMessage } from '@/lib/ipc-error'
// Re-export public API so existing consumers keep working.
@ -373,9 +376,9 @@ export function createPtyOutputProcessor({
): void {
const rawLength = meta?.rawLength ?? data.length
const suppressAttentionEvents = options.suppressAttentionEvents === true
// Why: OSC 9999 is a renderer-only control protocol. Parse it before
// xterm sees the bytes, and keep parser state across chunks so partial
// PTY reads do not drop valid status updates or print escape garbage.
// Why: OSC 9999 is an Orca control protocol. Parse it before xterm sees
// the bytes, and keep parser state across chunks so partial PTY reads do
// not drop valid status updates or print escape garbage.
const processed = processAgentStatusChunk(data)
data = processed.cleanData
// Why: mirror the onBell / onAgentBecameIdle guard below — during eager-buffer

View File

@ -1,4 +1,3 @@
import { createAgentStatusOscProcessor } from '@/components/terminal-pane/agent-status-osc'
import { subscribeToPtyData, subscribeToPtyExit } from '@/components/terminal-pane/pty-dispatcher'
import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client'
import { getRemoteRuntimeTerminalMultiplexer } from '@/runtime/remote-runtime-terminal-multiplexer'
@ -8,6 +7,7 @@ import {
getRemoteRuntimeTerminalHandle
} from '@/runtime/runtime-terminal-stream'
import { useAppStore } from '@/store'
import { createAgentStatusOscProcessor } from '../../../shared/agent-status-osc'
import type { ParsedAgentStatusPayload } from '../../../shared/agent-status-types'
export async function observeExistingAutomationSession(args: {

View File

@ -13,7 +13,6 @@ import {
subscribeToPtyData,
subscribeToPtyExit
} from '@/components/terminal-pane/pty-dispatcher'
import { createAgentStatusOscProcessor } from '@/components/terminal-pane/agent-status-osc'
import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client'
import { toRuntimeWorktreeSelector } from '@/runtime/runtime-worktree-selector'
import { singlePaneLayoutSnapshot } from '@/store/slices/terminal-helpers'
@ -22,6 +21,7 @@ import {
subscribeToRuntimeTerminalData,
toRemoteRuntimePtyId
} from '@/runtime/runtime-terminal-stream'
import { createAgentStatusOscProcessor } from '../../../shared/agent-status-osc'
import type { ParsedAgentStatusPayload } from '../../../shared/agent-status-types'
import type { RuntimeTerminalCreate } from '../../../shared/runtime-types'

View File

@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest'
import { createAgentStatusOscProcessor } from './agent-status-osc'
describe('createAgentStatusOscProcessor', () => {
it('strips OSC 9999 payloads from terminal data and returns parsed statuses', () => {
const process = createAgentStatusOscProcessor()
const result = process(
'before\x1b]9999;{"state":"working","prompt":"ship it","agentType":"codex"}\x07after'
)
expect(result.cleanData).toBe('beforeafter')
expect(result.payloads).toEqual([
{
state: 'working',
prompt: 'ship it',
agentType: 'codex'
}
])
})
it('preserves parser state across split OSC 9999 chunks', () => {
const process = createAgentStatusOscProcessor()
expect(process('before\x1b]999').cleanData).toBe('before')
const result = process('9;{"state":"done","prompt":"ok"}\x1b\\after')
expect(result.cleanData).toBe('after')
expect(result.payloads).toEqual([
{
state: 'done',
prompt: 'ok'
}
])
})
})

View File

@ -1,5 +1,5 @@
import type { ParsedAgentStatusPayload } from '../../../../shared/agent-status-types'
import { parseAgentStatusPayload } from '../../../../shared/agent-status-types'
import type { ParsedAgentStatusPayload } from './agent-status-types'
import { parseAgentStatusPayload } from './agent-status-types'
const OSC_AGENT_STATUS_PREFIX = '\x1b]9999;'
@ -28,8 +28,8 @@ function findAgentStatusTerminator(
/**
* Stateful OSC 9999 parser for PTY streams.
* Why: automation background launches need the same agent-status parsing as
* mounted terminal panes, even when no terminal has been rendered yet.
* Why: hidden/model-owned terminal output needs the same agent-status parsing
* as mounted terminal panes, even when no terminal view is rendered.
*/
export function createAgentStatusOscProcessor(): (data: string) => ProcessedAgentStatusChunk {
const MAX_PENDING = 64 * 1024