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]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 02:49:37 +08:00
1 parent daa85a8bb4
commit 459991421e
27 files changed
+1720 -32

No files matched your search

+24
View File
@@ -2,6 +2,7 @@ import type {
AgentBudgetKind,
AgentFailureReason,
AgentRunEvent,
AgentRunEventBody,
AgentUsageTotals,
CapabilityKind,
} from "@umt/contracts";
@@ -10,6 +11,11 @@ import type { AgentSettings } from "../settings.ts";
// 事件、用量、失败原因的类型统一取自 @umt/contracts(AgentRunEvent / AgentUsageTotals / AgentFailureReason /
// AgentBudgetKind);本文件只定义运行时端口。
export type SubagentEventBody = Extract<
AgentRunEventBody,
{ type: "subagent_spawned" | "subagent_event" | "subagent_completed" }
>;
/** 调用能力时使用的业务身份;授权回调据此到 automation_grant / approval 查询 */
export interface AgentActor {
actorId: string;
@@ -36,6 +42,17 @@ export interface CreateRunSpec {
* (agent_run_event 中无记录时为 0),保证 (run_id, seq) 不冲突;默认 0。
*/
firstSeq?: number;
/**
* 子代理:本会话派生的子会话已用掉的量(工具调用 / token / 费用)。父会话总预算含子会话用量——
* 运行时在每次工具调用与每轮请求前把它加到自己的用量上再比预算。由调用方缓存,须同步返回。
*/
externalUsage?: () => ExternalUsage;
}
export interface ExternalUsage {
toolCalls: number;
totalTokens: number;
costUsd: number;
}
export interface RunOutcome {
@@ -62,6 +79,13 @@ export interface AgentRunHandle {
/** 崩溃/重启后继续未完成的操作 */
resume(): Promise<RunOutcome>;
abort(reason?: string): Promise<void>;
/**
* 往本会话事件流追加一条子代理事件(subagent_*),与运行时自身事件共用 seq 计数(单写者)。
* 会话已关闭时返回 false,调用方自行落库。
*/
appendEvent(body: SubagentEventBody): boolean;
/** 本会话自身已用量(不含子会话) */
stats(): { toolCalls: number; usage: AgentUsageTotals };
compact(customInstructions?: string): Promise<void>;
close(): Promise<void>;
}