import { z } from "zod"; import type { AgentRunStatus } from "./agent.ts"; import { Id, JsonObject, NonNegInt, Timestamp } from "./common.ts"; import type { TypedEvent } from "./events.ts"; // Agent 运行事件(REQ §10.6):packages/agent 产生,server 先写 agent_run_event(带 seq)再推 SSE。 // 这里是唯一定义;agent 包的 onEvent 回调直接收 AgentRunEvent。 export const AgentRunEventType = z.enum([ "run_started", "text_delta", "thinking_delta", "tool_call_start", "tool_call_end", "tool_call_rejected", "compaction", "run_completed", "run_failed", "subagent_spawned", "subagent_event", "subagent_completed", ]); export type AgentRunEventType = z.infer; /** 一次运行累计用量(数值都齐全;写 agent_run.usage 时可直接用,是 AgentUsage 的子集形状) */ export const AgentUsageTotals = z.object({ inputTokens: NonNegInt, outputTokens: NonNegInt, cacheReadTokens: NonNegInt, cacheWriteTokens: NonNegInt, totalTokens: NonNegInt, costUsd: z.number().nonnegative(), }); export type AgentUsageTotals = z.infer; export const AgentFailureReason = z.enum(["budget", "aborted", "error"]); export type AgentFailureReason = z.infer; export const AgentBudgetKind = z.enum(["tool_calls", "duration", "tokens", "cost"]); export type AgentBudgetKind = z.infer; const RunStarted = z.object({ type: z.literal("run_started"), goal: z.string(), resumed: z.boolean() }); const TextDelta = z.object({ type: z.literal("text_delta"), delta: z.string() }); const ThinkingDelta = z.object({ type: z.literal("thinking_delta"), delta: z.string() }); const ToolCallStart = z.object({ type: z.literal("tool_call_start"), toolCallId: z.string(), capabilityId: z.string(), /** 已脱敏、截断的输入摘要 */ inputSummary: z.string(), /** 崩溃恢复后重放/补记的调用 */ recovery: z.boolean(), }); const ToolCallEnd = z.object({ type: z.literal("tool_call_end"), toolCallId: z.string(), capabilityId: z.string(), isError: z.boolean(), /** 已脱敏、截断的输出摘要 */ outputSummary: z.string(), recovery: z.boolean(), }); const ToolCallRejected = z.object({ type: z.literal("tool_call_rejected"), toolCallId: z.string(), toolName: z.string(), capabilityId: z.string().optional(), reason: z.string(), }); const Compaction = z.object({ type: z.literal("compaction"), phase: z.enum(["start", "end"]), reason: z.enum(["manual", "threshold", "overflow"]), status: z.enum(["completed", "declined", "aborted", "failed"]).optional(), }); const RunCompleted = z.object({ type: z.literal("run_completed"), finalText: z.string(), usage: AgentUsageTotals }); const RunFailed = z.object({ type: z.literal("run_failed"), reason: AgentFailureReason, message: z.string(), usage: AgentUsageTotals, budget: AgentBudgetKind.optional(), }); // 子代理(agent.spawn):子会话的事件镜像进父会话事件流,由父会话的运行进程按父 seq 写入(单写者,seq 不冲突)。 const SubagentSpawned = z.object({ type: z.literal("subagent_spawned"), childRunId: z.string(), goal: z.string(), mode: z.enum(["execute", "plan_only"]), depth: NonNegInt, capabilities: z.array(z.string()), /** 从父会话划拨给子会话的预算 */ budget: z.record(z.string(), z.number()), }); /** 子会话的一条事件(工具调用 / 开始 / 结束);childSeq 是子会话内的 seq,正文已脱敏截断 */ const SubagentEvent = z.object({ type: z.literal("subagent_event"), childRunId: z.string(), childSeq: NonNegInt, childType: z.string(), capabilityId: z.string().optional(), summary: z.string(), }); const SubagentCompleted = z.object({ type: z.literal("subagent_completed"), childRunId: z.string(), status: z.string(), /** 子会话汇报摘要(业务核查结论 + 未经核查的模型文本),已脱敏截断 */ summary: z.string(), usage: AgentUsageTotals, toolCalls: NonNegInt, }); /** 事件主体(不含 runId/seq/at) */ export const AgentRunEventBody = z.discriminatedUnion("type", [ RunStarted, TextDelta, ThinkingDelta, ToolCallStart, ToolCallEnd, ToolCallRejected, Compaction, RunCompleted, RunFailed, SubagentSpawned, SubagentEvent, SubagentCompleted, ]); export type AgentRunEventBody = z.infer; const EventMeta = { /** = agent_run.id */ runId: z.string().min(1), /** 该 run 内从 0 起单调递增;续跑时从 agent_run.last_event_seq + 1 继续 */ seq: NonNegInt, at: Timestamp, }; /** 完整事件:主体 + runId/seq/at */ export const AgentRunEvent = z.discriminatedUnion("type", [ RunStarted.extend(EventMeta), TextDelta.extend(EventMeta), ThinkingDelta.extend(EventMeta), ToolCallStart.extend(EventMeta), ToolCallEnd.extend(EventMeta), ToolCallRejected.extend(EventMeta), Compaction.extend(EventMeta), RunCompleted.extend(EventMeta), RunFailed.extend(EventMeta), SubagentSpawned.extend(EventMeta), SubagentEvent.extend(EventMeta), SubagentCompleted.extend(EventMeta), ]); export type AgentRunEvent = AgentRunEventBody & { runId: string; seq: number; at: string }; /** agent_run_event 行:(run_id, seq) 唯一;payload = 事件主体去掉 type */ export const AgentRunEventRow = z.object({ runId: Id, seq: NonNegInt, type: AgentRunEventType, at: Timestamp, payload: JsonObject, }); export type AgentRunEventRow = z.infer; export function agentRunEventToRow(e: AgentRunEvent): AgentRunEventRow { const { runId, seq, at, type, ...payload } = e; return { runId, seq, at, type, payload }; } /** 行 → 事件;payload 不合法时抛错(表里只应有 agentRunEventToRow 写入的内容) */ export function agentRunEventFromRow(row: AgentRunEventRow): AgentRunEvent { return AgentRunEvent.parse({ ...row.payload, type: row.type, runId: row.runId, seq: row.seq, at: row.at }); } /** 工具调用事件 → SSE agent.tool_call 的 phase */ export function toolCallPhase(e: AgentRunEventBody): "started" | "succeeded" | "failed" | "denied" | null { switch (e.type) { case "tool_call_start": return "started"; case "tool_call_end": return e.isError ? "failed" : "succeeded"; case "tool_call_rejected": return "denied"; default: return null; } } /** run_completed / run_failed / run_started → agent_run.status */ export function agentRunStatusOf(e: AgentRunEventBody): AgentRunStatus | null { switch (e.type) { case "run_started": return "running"; case "run_completed": return "succeeded"; case "run_failed": return e.reason === "budget" ? "budget_exceeded" : e.reason === "aborted" ? "cancelled" : "failed"; default: return null; } } /** * Agent 运行事件 → 全局 SSE 事件(agent.run_status / agent.text_delta / agent.tool_call)。 * thinking_delta 与 compaction 不上全局总线(只在 run 详情里按 agent_run_event 回放),返回 null。 */ export function agentRunEventToBusEvent(e: AgentRunEvent): TypedEvent | null { const phase = toolCallPhase(e); if (phase) { let capabilityId = ""; let summary = ""; if (e.type === "tool_call_start") [capabilityId, summary] = [e.capabilityId, e.inputSummary]; else if (e.type === "tool_call_end") [capabilityId, summary] = [e.capabilityId, e.outputSummary]; else if (e.type === "tool_call_rejected") [capabilityId, summary] = [e.capabilityId ?? e.toolName, e.reason]; return { type: "agent.tool_call", payload: { runId: e.runId, runSeq: e.seq, capabilityId, phase, summary } }; } // 子会话事件:借 agent.tool_call(能力 agent.spawn)提示父会话页"有新事件",页面按 seq 补读 if (e.type === "subagent_spawned" || e.type === "subagent_event" || e.type === "subagent_completed") { const phase = e.type === "subagent_completed" ? (e.status === "succeeded" ? "succeeded" : "failed") : ("started" as const); const summary = e.type === "subagent_spawned" ? `派生子会话 ${e.childRunId}:${e.goal}` : e.type === "subagent_event" ? `子会话 ${e.childRunId}:${e.childType} ${e.capabilityId ?? ""} ${e.summary}`.trim() : `子会话 ${e.childRunId} ${e.status}:${e.summary}`; return { type: "agent.tool_call", payload: { runId: e.runId, runSeq: e.seq, capabilityId: "agent.spawn", phase, summary: summary.slice(0, 600) }, }; } if (e.type === "text_delta") { return { type: "agent.text_delta", payload: { runId: e.runId, runSeq: e.seq, delta: e.delta } }; } const status = agentRunStatusOf(e); if (status) { const summary = e.type === "run_completed" ? e.finalText : e.type === "run_failed" ? e.message : null; return { type: "agent.run_status", payload: { runId: e.runId, status, summary } }; } return null; }