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:
1 parent
daa85a8bb4
commit
459991421e
27 files changed
+1720
-32
No files matched your search
@@ -186,6 +186,56 @@ export const InventoryQueryOutput = z.object({
|
||||
),
|
||||
});
|
||||
|
||||
// ── 子代理(W7-B) ──
|
||||
|
||||
/** 子会话预算:缺省按父会话剩余预算的一半划拨;超过剩余预算直接拒绝 */
|
||||
export const SubagentBudget = z.object({
|
||||
maxToolCalls: z.number().int().positive().optional(),
|
||||
maxTokens: z.number().int().positive().optional(),
|
||||
maxCostUsd: z.number().positive().optional(),
|
||||
maxDurationSec: z.number().int().positive().optional(),
|
||||
});
|
||||
export const AgentSpawnInput = z.object({
|
||||
goal: z.string().trim().min(1).max(4000),
|
||||
/** 为什么要派生(展示在会话树里) */
|
||||
reason: z.string().max(500).optional(),
|
||||
/** 必须是父会话能力白名单的子集;缺省继承父会话白名单 */
|
||||
capabilityAllowlist: z.array(z.string()).min(1).max(50).optional(),
|
||||
budget: SubagentBudget.optional(),
|
||||
/** 不得高于父会话:plan_only 的父会话只能派生 plan_only 子会话;缺省同父会话 */
|
||||
mode: z.enum(["execute", "plan_only"]).optional(),
|
||||
/** 覆盖子会话使用的 Agent 上游;缺省同父会话 */
|
||||
upstreamId: Id.optional(),
|
||||
/** true:派生后等它结束再返回(等待不调用模型) */
|
||||
await: z.boolean().default(false),
|
||||
timeoutSec: z.number().int().min(1).max(3600).default(600),
|
||||
});
|
||||
export const SubagentResult = z.object({
|
||||
runId: Id,
|
||||
goal: z.string(),
|
||||
status: z.string(),
|
||||
/** 汇报摘要(业务核查结论 + 未经核查的模型文本),已脱敏截断;未结束时为 null */
|
||||
summary: z.string().nullable(),
|
||||
toolCalls: NonNegInt,
|
||||
usage: z.object({ totalTokens: NonNegInt, costUsd: z.number().nonnegative() }),
|
||||
pending: z.boolean(),
|
||||
});
|
||||
export const AgentSpawnOutput = z.object({
|
||||
runId: Id,
|
||||
depth: NonNegInt,
|
||||
mode: z.enum(["execute", "plan_only"]),
|
||||
capabilities: z.array(z.string()),
|
||||
budget: SubagentBudget,
|
||||
/** await=true 时的结果 */
|
||||
result: SubagentResult.nullable(),
|
||||
});
|
||||
export const AgentAwaitInput = z.object({
|
||||
/** 缺省等本会话派生的全部子会话 */
|
||||
runIds: z.array(Id).min(1).max(20).optional(),
|
||||
timeoutSec: z.number().int().min(1).max(3600).default(600),
|
||||
});
|
||||
export const AgentAwaitOutput = z.object({ results: z.array(SubagentResult), allDone: z.boolean() });
|
||||
|
||||
/** 能力 ID → 输入/输出 schema(server 注册处理器、agent 生成工具定义时共用) */
|
||||
export const CAPABILITY_IO = {
|
||||
"catalog.lookup": { input: CatalogLookupInput, output: CatalogLookupOutput },
|
||||
@@ -203,6 +253,8 @@ export const CAPABILITY_IO = {
|
||||
"policy.preview": { input: PolicyPreviewInput, output: PolicyPreviewOutput },
|
||||
"policy.apply": { input: PolicyApplyInput, output: PolicyApplyOutput },
|
||||
"inventory.query": { input: InventoryQueryInput, output: InventoryQueryOutput },
|
||||
"agent.spawn": { input: AgentSpawnInput, output: AgentSpawnOutput },
|
||||
"agent.await": { input: AgentAwaitInput, output: AgentAwaitOutput },
|
||||
} as const satisfies Record<KnownCapabilityId, { input: z.ZodType; output: z.ZodType }>;
|
||||
|
||||
type CapabilityIo = typeof CAPABILITY_IO;
|
||||
@@ -252,3 +304,9 @@ export type PolicyApplyInput = z.infer<typeof PolicyApplyInput>;
|
||||
export type PolicyApplyOutput = z.infer<typeof PolicyApplyOutput>;
|
||||
export type InventoryQueryInput = z.infer<typeof InventoryQueryInput>;
|
||||
export type InventoryQueryOutput = z.infer<typeof InventoryQueryOutput>;
|
||||
export type AgentSpawnInput = z.infer<typeof AgentSpawnInput>;
|
||||
export type AgentSpawnOutput = z.infer<typeof AgentSpawnOutput>;
|
||||
export type AgentAwaitInput = z.infer<typeof AgentAwaitInput>;
|
||||
export type AgentAwaitOutput = z.infer<typeof AgentAwaitOutput>;
|
||||
export type SubagentResult = z.infer<typeof SubagentResult>;
|
||||
export type SubagentBudget = z.infer<typeof SubagentBudget>;
|
||||
Reference in new issue
Block a user