Files
UHD_Media_Tool/packages/contracts/test/agent-events.test.ts
T
rootandClaude Opus 5.5 459991421e feat(agent): Pi subagents via agent.spawn / agent.await
- agent_run gains depth / spawn_reason (0029); agent_run_event types
  subagent_spawned / subagent_event / subagent_completed
- agent.spawn (propose, medium, replay never): allowlist ⊆ parent, mode not
  above parent, budget carved from parent's remainder, idempotent per run+input;
  settings.pi.subagents {enabled, maxConcurrent, maxDepth} (subagentsEnabled kept as alias)
- child runs claim a concurrency slot under an advisory lock (waitForFact while
  queued); agent.await polls business tables without calling the model
- parent abort cascades to all descendants; parent runtime budget counts
  descendant usage; child events mirrored into the parent stream by parent seq
- authorize caps every child call by all ancestors' allowlists/kinds
- session page shows parent link, inherited info and the subagent tree
- e2e E11; runbook section on subagents

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 02:49:37 +08:00

88 lines
3.1 KiB
TypeScript

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 },
];
describe("agent 运行事件", () => {
it("样例覆盖全部 12 种事件类型", () => {
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",
]);
for (const b of bus) if (b) expect(C.parseEventPayload(b.type, b.payload), b.type).not.toBeNull();
});
});