// AgentStreamEventRouter — 统一的 agent 流式事件状态机 // 供 Page AI sidebar runtime 和 document editor adapter runtime 共用 // // 事件状态: // init — 建立 request_id 绑定 // loading — message_delta / tool_call_delta → 增量更新 // stream_event — tool-started / tool-finished / agent_state // finished — 正常终止 // interrupted — 中断(等待审批/恢复) // error — 错误终止 // approval_required — 等待用户确认 export const AGENT_EVENT_STATES = { INIT: 'init', LOADING: 'loading', STREAM_EVENT: 'stream_event', FINISHED: 'finished', INTERRUPTED: 'interrupted', ERROR: 'error', APPROVAL_REQUIRED: 'approval_required', }; export const TERMINAL_STATES = new Set([ AGENT_EVENT_STATES.FINISHED, AGENT_EVENT_STATES.INTERRUPTED, AGENT_EVENT_STATES.ERROR, ]); export function isTerminalState(status) { return TERMINAL_STATES.has(status); } // 解析 SSE 帧为 eventName + payloadText export function parseSSEFrames(buffer, lastBoundary) { var frames = buffer.split('\n\n'); var remaining = frames.pop() || ''; var events = []; frames.forEach(function(frame) { var eventName = ''; var dataLines = []; frame.split('\n').forEach(function(line) { if (line.startsWith('event:')) eventName = line.slice(6).trim(); if (line.startsWith('data:')) dataLines.push(line.slice(5).trim()); }); var payloadText = dataLines.join('\n'); if (!eventName && payloadText) { try { var parsed = JSON.parse(payloadText); eventName = parsed && parsed.event ? String(parsed.event) : ''; } catch (_) {} } if (eventName) events.push({ eventName: eventName, payloadText: payloadText }); }); return { events: events, remaining: remaining }; } // 从 SSE 流读取事件 export async function readSSEStream(response, onFrame) { if (!response.body || typeof response.body.getReader !== 'function') return; var reader = response.body.getReader(); var decoder = new TextDecoder(); var buffer = ''; while (true) { var chunk = await reader.read(); if (chunk.done) break; buffer += decoder.decode(chunk.value, { stream: true }); var result = parseSSEFrames(buffer, ''); buffer = result.remaining; result.events.forEach(function(evt) { onFrame(evt.eventName, evt.payloadText); }); } } // 创建标准事件路由器 // handlers: { onInit, onDelta, onToolCall, onToolResult, onAgentState, onTerminal, onApprovalRequired } export function createAgentStreamRouter(handlers) { var h = handlers || {}; return function routeEvent(eventName, payloadText) { var payload = null; try { payload = JSON.parse(payloadText || 'null'); } catch (_) {} var status = (payload && payload.status) || eventName; switch (status) { case AGENT_EVENT_STATES.INIT: if (h.onInit) h.onInit(payload); break; case AGENT_EVENT_STATES.LOADING: if (h.onDelta) h.onDelta(payload); if (h.onToolCall) { var toolChunks = (payload && payload.tool_call_chunks) || (payload && payload.msg && payload.msg.tool_call_chunks); if (toolChunks && toolChunks.length) h.onToolCall(payload); } break; case AGENT_EVENT_STATES.STREAM_EVENT: if (payload && payload.event === 'tool-finished') { if (h.onToolResult) h.onToolResult(payload); } else if (payload && payload.event === 'tool-started') { // tool start — 可选处理 } else if (payload && payload.agent_state) { if (h.onAgentState) h.onAgentState(payload.agent_state); } break; case AGENT_EVENT_STATES.FINISHED: case AGENT_EVENT_STATES.INTERRUPTED: case AGENT_EVENT_STATES.ERROR: if (h.onTerminal) h.onTerminal(status, payload); break; case AGENT_EVENT_STATES.APPROVAL_REQUIRED: if (h.onApprovalRequired) h.onApprovalRequired(payload); break; default: // 未知状态:尝试作为 loading 处理 if (payload && (payload.text || payload.delta || payload.content)) { if (h.onDelta) h.onDelta(payload); } break; } }; }