Sfoglia il codice sorgente

fix(subagent): default to eight live continuable children

Dudu-0223 3 settimane fa
parent
commit
d7f9d3a773

+ 2 - 2
.agents/notes/implemented/feature/2026-09-15-continuable-activation-capacity.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-09-15-continuable-activation-capacity.md
-2026-09-15-continuable-activation-capacity.md: 12dda97aae33609796b1121f00f6341303684d14
-2026-09-15-continuable-activation-capacity.zh.md: 99617f7809d92b31602f3092a2fb3c4bacca8a4c
+2026-09-15-continuable-activation-capacity.md: 71cdf421c09c80f232bcd5ef8bdd6f1dc2523edb
+2026-09-15-continuable-activation-capacity.zh.md: b55b379352f844297eb529bc32db693fd72642a6

+ 1 - 1
.agents/notes/implemented/feature/2026-09-15-continuable-activation-capacity.md

@@ -10,7 +10,7 @@ Depth limits bound nesting but permit wide concurrent delegation. Background Job
 
 ## Decision
 
-The subagent service configures `maxActiveSubagents`, defaulting to 16. Each live root has one process-local pool, shared by reference through its continuable Activations at every depth. The root itself is excluded. One-shot runs and external-provider work do not enter this pool. Delegation depth remains independently configured.
+The subagent service configures `maxActiveSubagents`, defaulting to 8. Each live root has one process-local pool, shared by reference through its continuable Activations at every depth. The root itself is excluded. One-shot runs and external-provider work do not enter this pool. Delegation depth remains independently configured.
 
 The Activation registry reserves a unique slot before fresh or cold-resume reconstruction yields. The materialization owns rollback until the Activation owns the slot; unpublished rollback and failed materialization may both release the same token safely. Handle disposal precedes release, which precedes parent settlement notification. Sending to an existing Activation reuses its slot.
 

+ 1 - 1
.agents/notes/implemented/feature/2026-09-15-continuable-activation-capacity.zh.md

@@ -10,7 +10,7 @@ Status: implemented
 
 ## Decision
 
-Subagent 服务通过 `maxActiveSubagents` 配置容量,默认值为 16。每个存活的根代理拥有一个进程内池,各层可续接 Activation 通过引用共享该池。根代理自身不计入。一次性运行和外部提供方工作不进入此池。委派深度仍独立配置。
+Subagent 服务通过 `maxActiveSubagents` 配置容量,默认值为 8。每个存活的根代理拥有一个进程内池,各层可续接 Activation 通过引用共享该池。根代理自身不计入。一次性运行和外部提供方工作不进入此池。委派深度仍独立配置。
 
 Activation registry 在新建或冷恢复重建首次让出执行前预占唯一名额。在 Activation 接管名额前,由 materialization 负责回滚;未发布回滚和失败的 materialization 可以安全地释放同一个 token。handle 释放先于名额归还,名额归还先于父代理完成通知。向已有 Activation 发送消息复用其名额。
 

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: ddfae5a3ce266c0e816c3a0e9879ad1bb8c74e3b
-config-catalog.zh.md: 103f1578da64234793a3c720c8841c6930cf8f84
+config-catalog.md: e93b6df09f865e8800c67faca75e8324e34eca48
+config-catalog.zh.md: ae676b0cc1904727dfbe71c57795bd8d87ff6695

+ 1 - 1
docs/config-catalog.md

@@ -2576,7 +2576,7 @@ Source: [`packages/storage/storage-sqlite/src/index.ts:24`](../packages/storage/
 ```ts config-catalog
 /** Host configuration for continuable subagent capacity. */
 export interface Config {
-  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 16. */
+  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 8. */
   maxActiveSubagents?: number
 }
 ```

+ 1 - 1
docs/config-catalog.zh.md

@@ -2578,7 +2578,7 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist'
 ```ts config-catalog
 /** Host configuration for continuable subagent capacity. */
 export interface Config {
-  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 16. */
+  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 8. */
   maxActiveSubagents?: number
 }
 ```

+ 2 - 2
packages/subagent/subagent/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md
-README.md: fc94f4e1e38141df7ccdd6579326d9367773dd46
-README.zh.md: 6e69321166991bdd74b6809d8a6e3d2c7399dee8
+README.md: ea542aba2fa482618d75342941043a4f3b6bd531
+README.zh.md: 57efd34abbc022b5ab3429c7a951bc679d3c1c69

+ 1 - 1
packages/subagent/subagent/README.md

@@ -44,7 +44,7 @@ An agent that calls the tool gets the child's final answer as the tool result. M
 
 ### Continuable capacity
 
-Set `maxActiveSubagents` on the host `dsh-subagent` plugin to limit live continuable children across each root Agent's entire continuable tree. It defaults to `16` and accepts positive safe integers. The root does not consume a slot; descendants inherit one shared pool. Fresh creation and cold resume reserve before reconstructing the Agent, and cleanup returns the slot after handle disposal. A waiting parent, pending inbox work, and an Activation being stopped still occupy slots. Messages to a resident child reuse its slot. One-shot and external-provider runs are outside this limit; depth remains the delegation tool's separate policy.
+Set `maxActiveSubagents` on the host `dsh-subagent` plugin to limit live continuable children across each root Agent's entire continuable tree. It defaults to `8` and accepts positive safe integers. The root does not consume a slot; descendants inherit one shared pool. Fresh creation and cold resume reserve before reconstructing the Agent, and cleanup returns the slot after handle disposal. A waiting parent, pending inbox work, and an Activation being stopped still occupy slots. Messages to a resident child reuse its slot. One-shot and external-provider runs are outside this limit; depth remains the delegation tool's separate policy.
 
 At capacity, creation or cold resume rejects with `ACTIVATION_LIMIT_REACHED`: wait for a child to finish or continue using the existing agents. Admission does not queue, because a parent waiting for descendants must not wait for its own occupied slot. Slots are process-local and do not constrain cumulative Session history or token usage.
 

+ 1 - 1
packages/subagent/subagent/README.zh.md

@@ -44,7 +44,7 @@ kind: "package-reference"
 
 ### 可续接子代理容量
 
-在 Host 的 `dsh-subagent` 插件上设置 `maxActiveSubagents`,限制每个根 Agent 的整棵可续接子代理树中存活的子代理数。默认值为 `16`,接受正安全整数。根 Agent 不占名额;后代继承同一个共享池。新建和冷恢复在重建 Agent 前预占名额,清理在 handle 释放后归还名额。等待后代的父代理、有待处理收件箱内容的代理以及正在停止的 Activation 仍占名额。向驻留子代理发送消息复用其名额。一次性和外部提供方运行不受此限制;深度仍由委派工具的独立策略决定。
+在 Host 的 `dsh-subagent` 插件上设置 `maxActiveSubagents`,限制每个根 Agent 的整棵可续接子代理树中存活的子代理数。默认值为 `8`,接受正安全整数。根 Agent 不占名额;后代继承同一个共享池。新建和冷恢复在重建 Agent 前预占名额,清理在 handle 释放后归还名额。等待后代的父代理、有待处理收件箱内容的代理以及正在停止的 Activation 仍占名额。向驻留子代理发送消息复用其名额。一次性和外部提供方运行不受此限制;深度仍由委派工具的独立策略决定。
 
 容量耗尽时,新建或冷恢复以 `ACTIVATION_LIMIT_REACHED` 拒绝:等待子代理完成,或继续使用现有代理。接纳不会排队,避免等待后代的父代理又等待自己占用的名额。名额仅存在于当前进程,不限制累计 Session 历史或 token 用量。
 

+ 2 - 2
packages/subagent/subagent/src/index.ts

@@ -187,14 +187,14 @@ interface BrowserPromptSource {
 
 /** Host configuration for continuable subagent capacity. */
 export interface Config {
-  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 16. */
+  /** Maximum live continuable children across one root's tree, excluding the root; defaults to 8. */
   maxActiveSubagents?: number
 }
 
 /** Named provider registry with one-shot runs, durable discovery, and continuable-child operations. */
 export class SubagentRuntime extends TypertRemoteService {
   static Config: z<Config> = z.object({
-    maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(16),
+    maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8),
   })
   private providers = new Map<string, SubagentProvider>()
   private continuations: SubagentContinuationManager | undefined

+ 3 - 3
packages/subagent/subagent/tests/continuation.spec.ts

@@ -261,16 +261,16 @@ describe('continuable activation capacity', () => {
     }
   })
 
-  it('defaults to sixteen live children and reuses capacity after settlement', async () => {
+  it('defaults to eight live children and reuses capacity after settlement', async () => {
     const release = Promise.withResolvers<undefined>()
     const adapter = new GatedAdapter([
-      ...Array.from({ length: 16 }, () => ({ chunks: textResponse('done'), gate: release.promise })),
+      ...Array.from({ length: 8 }, () => ({ chunks: textResponse('done'), gate: release.promise })),
       { chunks: textResponse('replacement') },
     ])
     const { ctx, parent } = await setupWith(adapter)
     parkParent(ctx, parent)
     try {
-      const started = await Promise.all(Array.from({ length: 16 }, () => ctx.subagents.startContinuable(startSpec(parent))))
+      const started = await Promise.all(Array.from({ length: 8 }, () => ctx.subagents.startContinuable(startSpec(parent))))
       await expect(ctx.subagents.startContinuable(startSpec(parent))).rejects.toMatchObject({ code: 'ACTIVATION_LIMIT_REACHED' })
       release.resolve(undefined)
       await Promise.all(started.map(child => waitNoActivation(ctx, child.childId)))