import { describe, expect, it } from "vitest"; import * as C from "../src/index.ts"; const RUN = "7f0f5d64-6a39-4a4e-9a86-0d3a2f1e8b11"; const AT = "2026-09-27T00:00:00.000Z"; const usage = { inputTokens: 1, outputTokens: 2, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 3, costUsd: 0 }; const samples: C.AgentRunEventBody[] = [ { type: "run_started", goal: "g", resumed: false }, { type: "text_delta", delta: "hi" }, { type: "thinking_delta", delta: "..." }, { type: "tool_call_start", toolCallId: "t1", capabilityId: "inventory.query", inputSummary: "{}", recovery: false }, { type: "tool_call_end", toolCallId: "t1", capabilityId: "inventory.query", isError: true, outputSummary: "x", recovery: false, }, { type: "tool_call_rejected", toolCallId: "t2", toolName: "bash", reason: "不在白名单" }, { type: "compaction", phase: "end", reason: "threshold", status: "completed" }, { type: "run_completed", finalText: "done", usage }, { type: "run_failed", reason: "budget", message: "超预算", usage, budget: "tokens" }, { type: "subagent_spawned", childRunId: RUN, goal: "子目标", mode: "plan_only", depth: 1, capabilities: ["inventory.query"], budget: { maxToolCalls: 5 }, }, { type: "subagent_event", childRunId: RUN, childSeq: 3, childType: "tool_call_end", capabilityId: "inventory.query", summary: "{}", }, { type: "subagent_completed", childRunId: RUN, status: "succeeded", summary: "ok", usage, toolCalls: 1 }, { type: "plan_updated", steps: [ { id: "s1", title: "查库存", status: "done", note: null }, { id: "s2", title: "搜资源", status: "running", note: "只查已开启站点" }, ], revision: 2, }, { type: "options_proposed", invocationId: "inv-1", decisionId: RUN, question: "收哪一个版本?", options: [ { id: "a", title: "REMUX", summary: "画质最好", risks: ["体积大"], estimatedCost: "78 GB" }, { id: "b", title: "WEB-DL", summary: "体积小", risks: [], estimatedCost: null }, ], recommendedId: "a", stepId: "s2", expiresAt: AT, timeoutAction: "recommended", }, { type: "options_resolved", invocationId: "inv-1", decisionId: RUN, outcome: "chosen", choice: "a", reason: null, by: "user", }, { type: "approval_requested", approvalId: RUN, decisionId: null, invocationId: "inv-2", capabilityId: "download.submit", planType: "acquisition_plan", planId: RUN, payloadHash: "h", expectedRevision: 1, riskLevel: "high", summary: "下载《沙丘2》", stepId: "s3", expiresAt: AT, }, { type: "approval_resolved", approvalId: RUN, status: "rejected", reason: "太大", decidedBy: "user:1" }, ]; describe("agent 运行事件", () => { it("样例覆盖全部 17 种事件类型", () => { expect(samples.map((s) => s.type).sort()).toEqual([...C.AgentRunEventType.options].sort()); }); it("行往返无损,且行 schema 合法", () => { samples.forEach((body, seq) => { const e = { ...body, runId: RUN, seq, at: AT } as C.AgentRunEvent; expect(C.AgentRunEvent.safeParse(e).success, body.type).toBe(true); const row = C.agentRunEventToRow(e); expect(C.AgentRunEventRow.safeParse(row).success, body.type).toBe(true); expect(row.payload).not.toHaveProperty("type"); expect(C.agentRunEventFromRow(row)).toEqual(e); }); }); it("映射到全局 SSE 事件:tool phase / run status,且 payload 合法", () => { const bus = samples.map((body, seq) => C.agentRunEventToBusEvent({ ...body, runId: RUN, seq, at: AT } as C.AgentRunEvent), ); expect( bus.map((b) => b ? `${b.type}:${"phase" in b.payload ? b.payload.phase : "status" in b.payload ? b.payload.status : ""}` : null, ), ).toEqual([ "agent.run_status:running", "agent.text_delta:", null, "agent.tool_call:started", "agent.tool_call:failed", "agent.tool_call:denied", null, "agent.run_status:succeeded", "agent.run_status:budget_exceeded", "agent.tool_call:started", "agent.tool_call:started", "agent.tool_call:succeeded", "agent.tool_call:succeeded", "agent.tool_call:started", "agent.tool_call:succeeded", "agent.tool_call:started", "agent.tool_call:denied", ]); for (const b of bus) if (b) expect(C.parseEventPayload(b.type, b.payload), b.type).not.toBeNull(); }); });