feat(agent): dedupe agent.spawn by normalized goal within a parent session (AG-4)

A spawn whose goal (NFKC, case, whitespace and punctuation folded) matches a
child of the same parent that is still queued/running, or succeeded/waiting
within the last 10 minutes, returns that child instead of creating a new one:
output gets reused=true, a message ("已有同目标子会话…") and the child's
current result summary. Failed/cancelled/over-budget children are not reused.
Spawn decisions are serialized per parent in-process so parallel duplicate
calls in one batch create a single child. Replays keyed by idempotency still
hit the original child as before. The subagent constraint in the system prompt
asks for Chinese sub-goals and no duplicate spawns.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 17:58:08 +08:00
1 parent 0508f3418f
commit 01653b0427
5 files changed
+178 -4

No files matched your search

@@ -390,6 +390,13 @@ export const SubagentResult = z.object({
pending: z.boolean(),
});
export const AgentSpawnOutput = z.object({
/**
* AG-4 去重:同一父会话里同一目标(归一化后)的子会话还在运行或 10 分钟内已完成时不新建,
* 返回已有子会话(reused = true,result 带它的当前结果摘要);界面的"派出 N 个子会话"不计这次调用
*/
reused: z.boolean().optional(),
/** 给模型的一句说明(reused 时:"已有同目标子会话…") */
message: z.string().optional(),
runId: Id,
depth: NonNegInt,
mode: z.enum(["execute", "plan_only"]),
+1 -1
View File
@@ -347,7 +347,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
"agent.spawn": d(
"agent.spawn",
"派生子会话",
"把一个有界子目标交给子会话处理:能力白名单须是本会话白名单的子集,预算从本会话剩余预算里划拨,模式不高于本会话;返回子会话 runId。await=true 时等它结束并返回汇报",
"把一个有界子目标交给子会话处理:能力白名单须是本会话白名单的子集,预算从本会话剩余预算里划拨,模式不高于本会话;返回子会话 runId。await=true 时等它结束并返回汇报。同一目标的子会话还在运行或 10 分钟内已完成时不新建,直接返回已有子会话(reused=true)",
"propose",
"AgentSpawn",
"organize.execute",