source dump of claude code
at main 134 lines 4.1 kB view raw
1import type { UUID } from 'crypto' 2import { randomUUID } from 'crypto' 3import { getIsNonInteractiveSession, getSessionId } from '../bootstrap/state.js' 4import type { SdkWorkflowProgress } from '../types/tools.js' 5 6type TaskStartedEvent = { 7 type: 'system' 8 subtype: 'task_started' 9 task_id: string 10 tool_use_id?: string 11 description: string 12 task_type?: string 13 workflow_name?: string 14 prompt?: string 15} 16 17type TaskProgressEvent = { 18 type: 'system' 19 subtype: 'task_progress' 20 task_id: string 21 tool_use_id?: string 22 description: string 23 usage: { 24 total_tokens: number 25 tool_uses: number 26 duration_ms: number 27 } 28 last_tool_name?: string 29 summary?: string 30 // Delta batch of workflow state changes. Clients upsert by 31 // `${type}:${index}` then group by phaseIndex to rebuild the phase tree, 32 // same fold as collectFromEvents + groupByPhase in PhaseProgress.tsx. 33 workflow_progress?: SdkWorkflowProgress[] 34} 35 36// Emitted when a foreground agent completes without being backgrounded. 37// Drained by drainSdkEvents() directly into the output stream — does NOT 38// go through the print.ts XML task_notification parser and does NOT trigger 39// the LLM loop. Consumers (e.g. VS Code session.ts) use this to remove the 40// task from the subagent panel. 41type TaskNotificationSdkEvent = { 42 type: 'system' 43 subtype: 'task_notification' 44 task_id: string 45 tool_use_id?: string 46 status: 'completed' | 'failed' | 'stopped' 47 output_file: string 48 summary: string 49 usage?: { 50 total_tokens: number 51 tool_uses: number 52 duration_ms: number 53 } 54} 55 56// Mirrors notifySessionStateChanged. The CCR bridge already receives this 57// via its own listener; SDK consumers (scmuxd, VS Code) need the same signal 58// to know when the main turn's generator is idle vs actively producing. 59// The 'idle' transition fires AFTER heldBackResult flushes and the bg-agent 60// do-while loop exits — so SDK consumers can trust it as the authoritative 61// "turn is over" signal even when result was withheld for background agents. 62type SessionStateChangedEvent = { 63 type: 'system' 64 subtype: 'session_state_changed' 65 state: 'idle' | 'running' | 'requires_action' 66} 67 68export type SdkEvent = 69 | TaskStartedEvent 70 | TaskProgressEvent 71 | TaskNotificationSdkEvent 72 | SessionStateChangedEvent 73 74const MAX_QUEUE_SIZE = 1000 75const queue: SdkEvent[] = [] 76 77export function enqueueSdkEvent(event: SdkEvent): void { 78 // SDK events are only consumed (drained) in headless/streaming mode. 79 // In TUI mode they would accumulate up to the cap and never be read. 80 if (!getIsNonInteractiveSession()) { 81 return 82 } 83 if (queue.length >= MAX_QUEUE_SIZE) { 84 queue.shift() 85 } 86 queue.push(event) 87} 88 89export function drainSdkEvents(): Array< 90 SdkEvent & { uuid: UUID; session_id: string } 91> { 92 if (queue.length === 0) { 93 return [] 94 } 95 const events = queue.splice(0) 96 return events.map(e => ({ 97 ...e, 98 uuid: randomUUID(), 99 session_id: getSessionId(), 100 })) 101} 102 103/** 104 * Emit a task_notification SDK event for a task reaching a terminal state. 105 * 106 * registerTask() always emits task_started; this is the closing bookend. 107 * Call this from any exit path that sets a task terminal WITHOUT going 108 * through enqueuePendingNotification-with-<task-id> (print.ts parses that 109 * XML into the same SDK event, so paths that do both would double-emit). 110 * Paths that suppress the XML notification (notified:true pre-set, kill 111 * paths, abort branches) must call this directly so SDK consumers 112 * (Scuttle's bg-task dot, VS Code subagent panel) see the task close. 113 */ 114export function emitTaskTerminatedSdk( 115 taskId: string, 116 status: 'completed' | 'failed' | 'stopped', 117 opts?: { 118 toolUseId?: string 119 summary?: string 120 outputFile?: string 121 usage?: { total_tokens: number; tool_uses: number; duration_ms: number } 122 }, 123): void { 124 enqueueSdkEvent({ 125 type: 'system', 126 subtype: 'task_notification', 127 task_id: taskId, 128 tool_use_id: opts?.toolUseId, 129 status, 130 output_file: opts?.outputFile ?? '', 131 summary: opts?.summary ?? '', 132 usage: opts?.usage, 133 }) 134}