// wire format emitted by the agent-wrapper CLI subcommand on the events.out // FIFO. every line on the FIFO is exactly one JSON object — parse line-by-line. // // this file is the source of truth for the sootsim ↔ agent channel. it's // imported by electron main (cockpit bridge), the CLI watch/transcript tools, // and the wrapper implementation itself, so it must stay runtime-safe (no // node-specific or electron-specific imports). export type AgentEvent = | { type: 'ready' sessionId: string projectId: string provider: 'codex' | 'claude' cwd: string ts: number } | { type: 'prompt-received' text: string inspectSummary?: string inspectTrace?: string ts: number } | { type: 'turn-started'; turnId?: string; ts: number } | { type: 'turn-reasoning'; delta: string; ts: number } | { type: 'turn-message'; delta: string; ts: number } | { type: 'turn-plan' steps: Array<{ id: string; title: string; status: string }> ts: number } | { type: 'tool-call'; name: string; args: unknown; ts: number } | { type: 'file-edited' path: string kind: 'add' | 'modify' | 'delete' diff?: string ts: number } | { type: 'file-diff-delta'; path: string; delta: string; ts: number } | { type: 'approval-needed'; kind: string; detail: unknown; ts: number } | { type: 'turn-completed' turnId?: string filesTouched: string[] durationMs: number costUsd?: number ts: number } | { type: 'error'; message: string; ts: number } | { type: 'exited'; code: number | null; ts: number } export type AgentEventType = AgentEvent['type'] export function isAgentEvent(value: unknown): value is AgentEvent { if (!value || typeof value !== 'object') return false const v = value as { type?: unknown; ts?: unknown } return typeof v.type === 'string' && typeof v.ts === 'number' } export function parseAgentEventLine(line: string): AgentEvent | null { const trimmed = line.trim() if (!trimmed) return null try { const parsed = JSON.parse(trimmed) as unknown return isAgentEvent(parsed) ? parsed : null } catch { return null } }