@deepseek-ai/dsh-subagent 0.1.3-alpha.2 → 0.1.5-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md
5
- README.md: 60aea7d446acf2e01f46132ab03354bf17f74213
6
- README.zh.md: e46ee53436256afa5f0c8dd1c635cbd0f9585927
5
+ README.md: 5f9668fd7a14cdfca33ab72a217c07372a24502f
6
+ README.zh.md: 563ed06237088c4bc250ca3c4f5a863b6ffe8239
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-subagent` is the service behind child-agent delegation: an agent hands a task to a named child, collects the finished result, and — for continuable children — keeps sending follow-up work across turns. Multiple providers coexist under one contract, so a single composition can offer in-process children, out-of-process ACP or SDK children, and real Codex or Claude Code children side by side. Children come in two shapes: one-shot runs that settle with a single result, and continuable children whose durable session accepts later messages and can be interrupted. The same service answers discovery questions — which children exist, their mode, activity, and lineage — without loading or resuming them. Mount it with at least one provider backend and a delegation tool; the backends and the model-facing tools live in sibling packages.
12
+ Use `dsh-subagent` to delegate work to named child agents, collect their results, and continue supported child conversations across turns. A composition can offer in-process, ACP, SDK, Codex, or Claude Code children side by side. Choose one-shot children for a single result or continuable children for later messages and interruption. You can also inspect available children, their mode, activity, and lineage without loading or resuming them. Enable at least one supported child backend and a delegation tool.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -96,6 +96,8 @@ A request is validated against the provider's advertised capabilities, a durable
96
96
 
97
97
  The manager reserves a child identity, resolves the durable descriptor, creates (or cold-resumes) the child Agent, installs it in an Activation, and submits the prompt. Model-authored messages cross one parent/child edge through fixed Steer scheduling; browser human prompts choose Queue or best-effort Steer through an internal adapter, while other host protocols may retain Queue for distinct turns. A Session queue command admits a live subagent-owned Agent only from its own continuable descriptor. Settlement waits for Agent activity to finish, an empty Inbox, and no owned children, then flushes final Session state with admission open. Under the child lock, the manager revalidates the wake generation, Session sequence, Inbox, and owned children; the synchronous task entry of `Agent.runMaintenance()` claims the idle phase and closes the private subagent Inbox in the same JavaScript turn before handle disposal. An absent direct-child Activation cold-resumes from the persisted session. When a resident Activation settles, the manager tells the child's direct parent in the parent's own turn stream.
98
98
 
99
+ Successful local child creation appends a `subagent/catalog` fact to the parent Session. One-shot creation records it after the provider returns; continuable creation records it after initial inbox admission and before returning the child id. Failure releases the child without publishing a compensating catalog event. A one-shot catalog append failure handles the run’s result rejection and preserves the catalog error; disposal failures are logged separately. The `subagentCatalog` projection excludes fork-inherited facts and exposes a direct-child list through `projections.values.subagentCatalog` in Session observations and client snapshots. Invalid own catalog payloads, including unsupported versions, reject projection restoration. Its immutable storage and checkpoint validation use [`dsh-chunked-list`](../../util/chunked-list/README.md). Its view preserves parent catalog event order in O(D) time for D facts. [The parent-catalog decision](../../../.agents/notes/implemented/architecture/2026-09-01-parent-owned-subagent-catalog.md) owns ordering, persistence costs, and alternatives.
100
+
99
101
  ### Ownership and invariants
100
102
 
101
103
  - **Publication is the boundary** — before it the provider owns the setup and must roll back on failure; after it the caller owns the run and must dispose it.
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-subagent` 是子 agent 委派背后的服务:agent(智能体)把任务交给具名子 agent,收集完成的结果,并且——对可继续子 agent 而言——跨轮次持续发送后续工作。多个提供方在同一约定下共存,因此单个组合可以并排提供进程内子 agent、进程外 ACP 或 SDK 子 agent,以及真实 Codex 或 Claude Code 子 agent。子 agent 有两种形态:一次性运行以单个结果结算,可继续子 agent 的持久会话则接受后续消息并可被中断。同一服务还回答发现类问题——存在哪些子级、它们的模式、活动状态与血缘——而不加载或恢复它们。把它与至少一个提供方后端和一个委派工具一起挂载;后端与面向模型的工具位于兄弟包中。
12
+ 使用 `dsh-subagent` 把工作委派给具名子 agent、收集结果,并跨轮次继续受支持的子级对话。一个组合可以并排提供进程内、ACP、SDK、Codex 或 Claude Code 子级。需要单个结果时选择一次性子级;需要后续消息与中断能力时选择可继续子级。你还可以检查可用子级及其模式、活动状态与血缘,而无需加载或恢复它们。启用时需要至少一个受支持的子级后端和一个委派工具。
13
13
 
14
14
  ## 目录
15
15
 
@@ -96,6 +96,8 @@ kind: "package-reference"
96
96
 
97
97
  管理器预留 child 身份、解析持久化描述符、创建(或冷恢复)child、把它安装进 Activation 并提交提示词。模型编写的消息通过固定 Steer 调度跨一条 parent/child 边;浏览器人类 prompt 通过内部适配器选择 Queue 或 best-effort Steer,其他 host 协议仍可保留 Queue 以创建独立轮次。Session queue command 仅根据 child 自身的 continuable descriptor 准入在线 subagent-owned Agent。Settlement 会等待 Agent 活动结束、Inbox 为空且没有所拥有子级,再在准入开放时 flush 最终 Session 状态。管理器随后在 child lock 内重新验证 wake generation、Session 序号、Inbox 与所拥有子级;`Agent.runMaintenance()` 的同步 task 入口会占用 idle 阶段,并在同一个 JavaScript turn 内关闭私有 subagent Inbox,然后才 dispose handle。直接 child 不存在 Activation 时会从持久化会话冷恢复。当驻留 Activation 结算时,管理器会在 parent 自身的轮次流中告知该 child 的直接 parent。
98
98
 
99
+ 本地子级创建成功时,父 Session 追加一条 `subagent/catalog` 事实。一次性创建在 provider 返回后记录;可继续创建在初始 inbox 准入后、返回子级 id 前记录。失败会释放子级,不发布补偿性目录事件。一次性目录追加失败时会处理 run 的结果拒绝,并保留目录错误;资源释放失败会单独记录。`subagentCatalog` projection 排除 fork 继承的事实,通过 Session 观察和客户端快照中的 `projections.values.subagentCatalog` 暴露直接子级列表。无效的自身 catalog payload(包括不支持的版本)会使 projection 恢复失败。其不可变存储和检查点校验使用 [`dsh-chunked-list`](../../util/chunked-list/README.zh.md)。其视图对 D 条事实以 O(D) 时间保留父目录事件顺序。[父目录决策](../../../.agents/notes/implemented/architecture/2026-09-01-parent-owned-subagent-catalog.zh.md) 说明排序、持久化成本和替代方案。
100
+
99
101
  ### 所有权与不变式
100
102
 
101
103
  - **发布即边界**——发布前提供方拥有设置并须在失败时回滚;发布后调用方拥有运行并须 dispose(资源释放)它。
package/lib/index.js CHANGED
@@ -10,6 +10,7 @@ import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
10
10
  import { SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
11
11
  import { brandString } from "@deepseek-ai/dsh-brand";
12
12
  import { snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
13
+ import { appendChunkedList, chunkedListSchema, iterateChunkedList } from "@deepseek-ai/dsh-chunked-list";
13
14
  import { accessSync, constants, statSync } from "node:fs";
14
15
  import { isAbsolute, resolve } from "node:path";
15
16
  //#region lib/types/error.js
@@ -708,7 +709,7 @@ var SubagentInbox = class {
708
709
  * @returns whether either Agent inbox destination is non-empty.
709
710
  */
710
711
  get hasPending() {
711
- return this.agent.inbox.hasPending;
712
+ return this.agent.inbox.nextTurn.length > 0 || this.agent.inbox.nextStep.length > 0;
712
713
  }
713
714
  /**
714
715
  * Submit through the Agent only while its Activation remains resident.
@@ -1004,7 +1005,6 @@ var ContinuableActivationRegistry = class {
1004
1005
  } finally {
1005
1006
  this.wake(activation);
1006
1007
  }
1007
- activation.announced = true;
1008
1008
  return message.id;
1009
1009
  }
1010
1010
  /**
@@ -1060,8 +1060,7 @@ var ContinuableActivationRegistry = class {
1060
1060
  async materializeTracked(inputs, parentLineage) {
1061
1061
  const { childId, provider, parent, create } = inputs;
1062
1062
  inputs.signal.throwIfAborted();
1063
- const setup = (childCtx) => {
1064
- const child = childCtx.agent;
1063
+ const setup = (childCtx, child) => {
1065
1064
  if (create !== void 0) {
1066
1065
  child.session.append("subagent/descriptor", create.descriptor);
1067
1066
  appendDelegatedPolicyOverrides(child.session, create.delegatedPolicies);
@@ -1071,11 +1070,13 @@ var ContinuableActivationRegistry = class {
1071
1070
  const observer = this.observeActivation(provider, childId, parent);
1072
1071
  const handle = create === void 0 ? await this.ownerCtx.agents.resume({
1073
1072
  resumeSessionId: childId,
1073
+ parentAgent: parent,
1074
1074
  agentOptions: inputs.agentOptions,
1075
1075
  signal: inputs.signal,
1076
1076
  setup
1077
1077
  }) : await this.ownerCtx.agents.create({
1078
1078
  sessionId: childId,
1079
+ parentAgent: parent,
1079
1080
  meta: create.meta,
1080
1081
  ...create.seed === void 0 ? {} : { seed: create.seed },
1081
1082
  inheritedEventCount: create.inheritedEventCount,
@@ -1426,6 +1427,100 @@ function foldSubagentDescriptor(events) {
1426
1427
  if (event === void 0) return void 0;
1427
1428
  return parseSubagentDescriptor(event.data);
1428
1429
  }
1430
+ const sessionIdSchema = z.string();
1431
+ const oneShotCatalogSchema = z.object({
1432
+ version: z.literal(0),
1433
+ childId: sessionIdSchema,
1434
+ childCreatedAt: z.number().int().nonnegative(),
1435
+ mode: z.literal("one-shot"),
1436
+ label: z.string().optional()
1437
+ }).strict();
1438
+ const continuableCatalogSchema = z.object({
1439
+ version: z.literal(0),
1440
+ childId: sessionIdSchema,
1441
+ childCreatedAt: z.number().int().nonnegative(),
1442
+ mode: z.literal("continuable"),
1443
+ label: z.string()
1444
+ }).strict();
1445
+ const eventDataSchema = z.union([oneShotCatalogSchema, continuableCatalogSchema]);
1446
+ const viewSchema = z.array(z.union([oneShotCatalogSchema.omit({
1447
+ version: true,
1448
+ childId: true,
1449
+ childCreatedAt: true
1450
+ }).extend({
1451
+ id: sessionIdSchema,
1452
+ createdAt: oneShotCatalogSchema.shape.childCreatedAt
1453
+ }), continuableCatalogSchema.omit({
1454
+ version: true,
1455
+ childId: true,
1456
+ childCreatedAt: true
1457
+ }).extend({
1458
+ id: sessionIdSchema,
1459
+ createdAt: continuableCatalogSchema.shape.childCreatedAt
1460
+ })]));
1461
+ const stateSchema = z.object({
1462
+ inheritedEventCount: z.number().int().nonnegative(),
1463
+ head: chunkedListSchema(eventDataSchema).optional()
1464
+ }).strict();
1465
+ /**
1466
+ * Materialize direct children from their parent's successful creation facts.
1467
+ * @param state - parent catalog fold state.
1468
+ * @returns current direct-child rows in parent catalog event order.
1469
+ */
1470
+ function subagentCatalogEntries(state) {
1471
+ const entries = [];
1472
+ for (const data of iterateChunkedList(state.head)) entries.push(data.mode === "one-shot" ? {
1473
+ id: data.childId,
1474
+ createdAt: data.childCreatedAt,
1475
+ mode: data.mode,
1476
+ ...data.label === void 0 ? {} : { label: data.label }
1477
+ } : {
1478
+ id: data.childId,
1479
+ createdAt: data.childCreatedAt,
1480
+ mode: data.mode,
1481
+ label: data.label
1482
+ });
1483
+ return entries;
1484
+ }
1485
+ /** Parent-owned direct-child catalog projection; invalid own facts reject restoration. */
1486
+ const subagentCatalogProjectionDefinition = {
1487
+ key: "subagentCatalog",
1488
+ stateSchema,
1489
+ init: (_header, inheritedEventCount) => ({ inheritedEventCount }),
1490
+ apply: (state, event) => {
1491
+ if (event.type !== "subagent/catalog" || event.seq < state.inheritedEventCount) return state;
1492
+ return {
1493
+ ...state,
1494
+ head: appendChunkedList(state.head, eventDataSchema.parse(event.data))
1495
+ };
1496
+ },
1497
+ stateVersion: 2,
1498
+ wire: {
1499
+ viewSchema,
1500
+ view: subagentCatalogEntries
1501
+ }
1502
+ };
1503
+ /**
1504
+ * Append a complete direct-child discovery fact to its parent Session.
1505
+ * @param parent - durable direct parent receiving the discovery fact.
1506
+ * @param child - established child's immutable Session metadata.
1507
+ * @param descriptor - mode-discriminated creation label frozen with the child.
1508
+ */
1509
+ function establishCatalogChild(parent, child, descriptor) {
1510
+ parent.append("subagent/catalog", descriptor.mode === "one-shot" ? {
1511
+ version: 0,
1512
+ childId: child.id,
1513
+ childCreatedAt: child.createdAt,
1514
+ mode: descriptor.mode,
1515
+ ...descriptor.label === void 0 ? {} : { label: descriptor.label }
1516
+ } : {
1517
+ version: 0,
1518
+ childId: child.id,
1519
+ childCreatedAt: child.createdAt,
1520
+ mode: descriptor.mode,
1521
+ label: descriptor.label
1522
+ });
1523
+ }
1429
1524
  //#endregion
1430
1525
  //#region lib/types/internal.js
1431
1526
  /**
@@ -1611,11 +1706,14 @@ var SubagentContinuationManager = class {
1611
1706
  },
1612
1707
  signal: spec.signal
1613
1708
  });
1614
- return this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get("tools")?.get("send_message", activation.handle.agent)) ? withContinuableReturnGuidance(parent.id, request.prompt) : request.prompt, {
1709
+ const childHeader = activation.handle.agent.session.header;
1710
+ return await this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get("tools")?.get("send_message", activation.handle.agent)) ? withContinuableReturnGuidance(parent.id, request.prompt) : request.prompt, {
1615
1711
  source: { kind: "user" },
1616
1712
  signal: spec.signal,
1617
1713
  delivery: "queue"
1618
- }, parent);
1714
+ }, parent, () => {
1715
+ establishCatalogChild(parent.session, childHeader, descriptor);
1716
+ });
1619
1717
  })
1620
1718
  };
1621
1719
  } catch (error) {
@@ -1707,7 +1805,9 @@ var SubagentContinuationManager = class {
1707
1805
  return;
1708
1806
  }
1709
1807
  }
1710
- return this.submitAdmitted(activation, content, options, parent);
1808
+ const messageId = this.submitAdmitted(activation, content, options, parent);
1809
+ activation.announced = true;
1810
+ return messageId;
1711
1811
  });
1712
1812
  /* v8 ignore start -- only a delivery that lost the disposal cutoff retries. */
1713
1813
  if (live !== void 0) return live;
@@ -1817,18 +1917,23 @@ var SubagentContinuationManager = class {
1817
1917
  __disposeResources$1(env_1);
1818
1918
  }
1819
1919
  }
1820
- /** Submit to a freshly materialized Activation or roll it back completely. */
1821
- async submitMaterialized(activation, content, options, parent) {
1920
+ /** Admit a materialized child, commit its creation fact, and release it on failure. */
1921
+ async submitMaterialized(activation, content, options, parent, commit) {
1822
1922
  try {
1823
1923
  if (contentHasImage(content)) {
1824
1924
  await this.assertImageCapable(activation.handle.agent, options.signal);
1825
1925
  if (activation.inbox.closing !== void 0) throw new SubagentError(`subagent "${activation.childId}" is closing`, "ACTIVATION_CLOSING");
1826
1926
  }
1827
- return this.submitAdmitted(activation, content, options, parent);
1927
+ const messageId = this.submitAdmitted(activation, content, options, parent);
1928
+ commit?.();
1929
+ activation.announced = true;
1930
+ return messageId;
1828
1931
  } catch (error) {
1829
- /* v8 ignore next -- rollback disposal failures must not mask the
1830
- * pre-acceptance signal, drain, or lifecycle failure. */
1831
- await this.activations.dispose(activation).catch(() => void 0);
1932
+ try {
1933
+ await this.activations.dispose(activation);
1934
+ } catch (cleanupError) {
1935
+ this.ctx.logger.warn(`subagent continuation: disposal after admission or catalog append failure also failed: ${String(cleanupError)}`);
1936
+ }
1832
1937
  throw error;
1833
1938
  }
1834
1939
  }
@@ -2759,6 +2864,7 @@ let SubagentRuntime = (() => {
2759
2864
  }, "subagents.continuationBinding()");
2760
2865
  });
2761
2866
  ctx.inject(["sessionProjections"], (projectionCtx) => {
2867
+ projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
2762
2868
  projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
2763
2869
  projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
2764
2870
  });
@@ -3030,6 +3136,8 @@ let SubagentRuntime = (() => {
3030
3136
  * fulfills; a rejection therefore has no run for the caller to dispose and
3031
3137
  * emits no run lifecycle events. Post-publication turn and infrastructure
3032
3138
  * failures settle through the returned run.
3139
+ * A catalog append failure disposes the run and handles its result rejection;
3140
+ * the caller receives the catalog error even if disposal also fails.
3033
3141
  * @param name - the provider to use.
3034
3142
  * @param request - child label, prompt, parent, signal, and optional capabilities.
3035
3143
  * @returns the published holder-owned run.
@@ -3048,7 +3156,20 @@ let SubagentRuntime = (() => {
3048
3156
  ...request,
3049
3157
  descriptor
3050
3158
  };
3051
- return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
3159
+ const run = await provider.start(resolved);
3160
+ const child = run.localAgent?.session;
3161
+ if (child !== void 0) try {
3162
+ establishCatalogChild(request.parent.session, child.header, descriptor);
3163
+ } catch (error) {
3164
+ run.result.catch(() => void 0);
3165
+ try {
3166
+ await run.dispose();
3167
+ } catch (cleanupError) {
3168
+ this.ctx.logger.warn(`subagent: disposal after catalog append failure also failed: ${String(cleanupError)}`);
3169
+ }
3170
+ throw error;
3171
+ }
3172
+ return observeRun(this.emitLifecycle, name, request.parent, run);
3052
3173
  }
3053
3174
  /**
3054
3175
  * Resolve one provider's detached continuable-creation contribution. Method
@@ -102,7 +102,7 @@ export const TYPERT = {
102
102
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentInterruptReceipt',
103
103
  schema: _deepseek_ai_dsh_subagent_subagents_interruptByParent_result$schema,
104
104
  },
105
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":477,"column":3},
105
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":480,"column":3},
106
106
  },
107
107
  {
108
108
  id: '@deepseek-ai/dsh-subagent#subagents/list',
@@ -129,7 +129,7 @@ export const TYPERT = {
129
129
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentCatalog',
130
130
  schema: _deepseek_ai_dsh_subagent_subagents_list_result$schema,
131
131
  },
132
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":383,"column":9},
132
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":386,"column":9},
133
133
  },
134
134
  {
135
135
  id: '@deepseek-ai/dsh-subagent#subagents/prompt',
@@ -155,7 +155,7 @@ export const TYPERT = {
155
155
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentPromptReceipt',
156
156
  schema: _deepseek_ai_dsh_subagent_subagents_prompt_result$schema,
157
157
  },
158
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":410,"column":9},
158
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":413,"column":9},
159
159
  },
160
160
  ],
161
161
  model: {
@@ -264,7 +264,7 @@ export const TYPERT = {
264
264
  "name": "start",
265
265
  "signature": "async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>",
266
266
  "summary": "Establish a published child on the named provider.",
267
- "jsDoc": "/**\n * Establish a published child on the named provider. Capability and semantic\n * checks run before delegation. Provider ownership lasts until its promise\n * fulfills; a rejection therefore has no run for the caller to dispose and\n * emits no run lifecycle events. Post-publication turn and infrastructure\n * failures settle through the returned run.\n * @param name - the provider to use.\n * @param request - child label, prompt, parent, signal, and optional capabilities.\n * @returns the published holder-owned run.\n */"
267
+ "jsDoc": "/**\n * Establish a published child on the named provider. Capability and semantic\n * checks run before delegation. Provider ownership lasts until its promise\n * fulfills; a rejection therefore has no run for the caller to dispose and\n * emits no run lifecycle events. Post-publication turn and infrastructure\n * failures settle through the returned run.\n * A catalog append failure disposes the run and handles its result rejection;\n * the caller receives the catalog error even if disposal also fails.\n * @param name - the provider to use.\n * @param request - child label, prompt, parent, signal, and optional capabilities.\n * @returns the published holder-owned run.\n */"
268
268
  }
269
269
  ],
270
270
  "types": [
@@ -386,7 +386,15 @@ export const TYPERT = {
386
386
  },
387
387
  {
388
388
  "name": "EpochHeader",
389
- "declaration": "export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n system?: string;\n tools?: ToolSchema[];\n}"
389
+ "declaration": "export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n tools?: ToolSchema[];\n}"
390
+ },
391
+ {
392
+ "name": "FeedbackCategory",
393
+ "declaration": "export type FeedbackCategory = 'task-result' | 'instruction-following' | 'product-interaction' | 'service-stability' | 'resource-cost' | 'security-privacy-permission' | 'other';"
394
+ },
395
+ {
396
+ "name": "FeedbackRecord",
397
+ "declaration": "export interface FeedbackRecord {\n readonly text?: string;\n readonly category?: FeedbackCategory;\n}"
390
398
  },
391
399
  {
392
400
  "name": "FileAttachmentRef",
@@ -458,7 +466,7 @@ export const TYPERT = {
458
466
  },
459
467
  {
460
468
  "name": "Inbox",
461
- "declaration": "export class Inbox {\n get nextTurn(): readonly UserMessage[];\n get nextStep(): readonly UserMessage[];\n get hasPending(): boolean;\n clear(): void;\n claim(target: InboxTarget, turn: number): UserMessage[];\n append(target: InboxTarget, message: UserMessage): void;\n prepend(target: InboxTarget, message: UserMessage): void;\n replace(messageId: MessageId, newMessage: UserMessage): boolean;\n remove(messageId: MessageId): boolean;\n splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];\n}"
469
+ "declaration": "export interface Inbox {\n readonly nextTurn: readonly UserMessage[];\n readonly nextStep: readonly UserMessage[];\n clear(): void;\n append(target: InboxTarget, message: UserMessage): void;\n prepend(target: InboxTarget, message: UserMessage): void;\n replace(messageId: MessageId, newMessage: UserMessage): boolean;\n remove(messageId: MessageId): boolean;\n splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];\n}"
462
470
  },
463
471
  {
464
472
  "name": "InboxTarget",
@@ -502,7 +510,7 @@ export const TYPERT = {
502
510
  },
503
511
  {
504
512
  "name": "MessageFeedbackItem",
505
- "declaration": "export interface MessageFeedbackItem {\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly version: MessageFeedbackVersion;\n readonly createdAt: number;\n readonly updatedAt: number;\n}"
513
+ "declaration": "export interface MessageFeedbackItem {\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly category?: FeedbackCategory;\n readonly version: MessageFeedbackVersion;\n readonly createdAt: number;\n readonly updatedAt: number;\n}"
506
514
  },
507
515
  {
508
516
  "name": "MessageFeedbackPut",
@@ -578,7 +586,7 @@ export const TYPERT = {
578
586
  },
579
587
  {
580
588
  "name": "RequestContext",
581
- "declaration": "export interface RequestContext {\n provider: string;\n model: string;\n contextWindow?: number;\n}"
589
+ "declaration": "export interface RequestContext {\n provider: string;\n model: string;\n contextWindow?: number;\n systemPromptUpdate?: SystemPromptUpdate;\n}"
582
590
  },
583
591
  {
584
592
  "name": "RequestHeaderReason",
@@ -598,11 +606,11 @@ export const TYPERT = {
598
606
  },
599
607
  {
600
608
  "name": "SessionEvent",
601
- "declaration": "export type SessionEvent<T extends SessionEventType = SessionEventType> = { [K in SessionEventType]: { type: K; seq: SessionSeq; time: number; data: SessionEventMap[K]; ignorable?: true; } & (K extends SurfaceEventType ? { sourceEventSeqs?: SessionSeq[]; surfaceOp?: SurfaceOp; } : object) }[T];"
609
+ "declaration": "export type SessionEvent<T extends SessionEventType = SessionEventType> = { [K in SessionEventType]: { type: K; seq: SessionSeq; time: number; data: SessionEventMap[K]; ignorable?: true; } & (K extends SurfaceEventType ? SurfaceIntent<K> : { surfaceOp?: never; sourceEventSeqs?: never; }) }[T];"
602
610
  },
603
611
  {
604
612
  "name": "SessionEventMap",
605
- "declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/code-dispatch-start': PtcDispatchStartEventData;\n 'tool/code-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'team/member': { version: 2; teamId: TeamId; member: TeamMemberSnapshot; };\n 'team/task': { version: 2; teamId: TeamId; task: TeamTaskSnapshot; };\n 'team/message/queued': { version: 2; teamId: TeamId; message: TeamMessageSnapshot; };\n 'team/message/delivered': { version: 2; teamId: TeamId; messageId: TeamMessageId; targetId: SessionId; };\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n}"
613
+ "declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'subagent/catalog': SubagentCatalogEvent;\n 'feedback/record': FeedbackRecord;\n 'team/member': { version: 2; teamId: TeamId; member: TeamMemberSnapshot; };\n 'team/task': { version: 2; teamId: TeamId; task: TeamTaskSnapshot; };\n 'team/message/queued': { version: 2; teamId: TeamId; message: TeamMessageSnapshot; };\n 'team/message/delivered': { version: 2; teamId: TeamId; messageId: TeamMessageId; targetId: SessionId; };\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n}"
606
614
  },
607
615
  {
608
616
  "name": "SessionEventType",
@@ -668,6 +676,10 @@ export const TYPERT = {
668
676
  "name": "SubagentCatalog",
669
677
  "declaration": "export interface SubagentCatalog {\n readonly entries: readonly SubagentListEntry[];\n readonly parentAvailable: boolean;\n}"
670
678
  },
679
+ {
680
+ "name": "SubagentCatalogEvent",
681
+ "declaration": "export type SubagentCatalogEvent = { readonly version: 0; readonly childId: SessionId; readonly childCreatedAt: number; } & ({ readonly mode: 'one-shot'; readonly label?: string; } | { readonly mode: 'continuable'; readonly label: string; });"
682
+ },
671
683
  {
672
684
  "name": "SubagentDescendantListEntry",
673
685
  "declaration": "export type SubagentDescendantListEntry = SubagentListEntry & { readonly parentId: SessionId; readonly depth: number; };"
@@ -738,7 +750,7 @@ export const TYPERT = {
738
750
  },
739
751
  {
740
752
  "name": "SurfaceEventType",
741
- "declaration": "export type SurfaceEventType = 'user/message' | 'assistant/message' | 'tool/result';"
753
+ "declaration": "export type SurfaceEventType = 'system/message' | 'user/message' | 'assistant/message' | 'tool/result';"
742
754
  },
743
755
  {
744
756
  "name": "SurfaceIntent",
@@ -746,7 +758,15 @@ export const TYPERT = {
746
758
  },
747
759
  {
748
760
  "name": "SurfaceOp",
749
- "declaration": "export type SurfaceOp = 'append' | { op: 'replace'; start: SessionSeq; end: SessionSeq; };"
761
+ "declaration": "export type SurfaceOp = 'append' | { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq; };"
762
+ },
763
+ {
764
+ "name": "SystemMessage",
765
+ "declaration": "export interface SystemMessage extends Message {\n readonly role: 'system';\n readonly source: MessageSourceMap['plugin'];\n}"
766
+ },
767
+ {
768
+ "name": "SystemPromptUpdate",
769
+ "declaration": "export type SystemPromptUpdate = 'in-history';"
750
770
  },
751
771
  {
752
772
  "name": "TeamId",
@@ -99,7 +99,7 @@ export const TYPERT_REMOTE = {
99
99
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentInterruptReceipt',
100
100
  schema: _deepseek_ai_dsh_subagent_subagents_interruptByParent_result$schema,
101
101
  },
102
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":477,"column":3},
102
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":480,"column":3},
103
103
  },
104
104
  {
105
105
  id: '@deepseek-ai/dsh-subagent#subagents/list',
@@ -126,7 +126,7 @@ export const TYPERT_REMOTE = {
126
126
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentCatalog',
127
127
  schema: _deepseek_ai_dsh_subagent_subagents_list_result$schema,
128
128
  },
129
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":383,"column":9},
129
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":386,"column":9},
130
130
  },
131
131
  {
132
132
  id: '@deepseek-ai/dsh-subagent#subagents/prompt',
@@ -152,7 +152,7 @@ export const TYPERT_REMOTE = {
152
152
  typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentPromptReceipt',
153
153
  schema: _deepseek_ai_dsh_subagent_subagents_prompt_result$schema,
154
154
  },
155
- sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":410,"column":9},
155
+ sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":413,"column":9},
156
156
  },
157
157
  ],
158
158
  }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Parent-owned durable subagent catalog events and their chunked projection.
3
+ *
4
+ * @module @deepseek-ai/dsh-subagent/catalog
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ChunkedList } from '@deepseek-ai/dsh-chunked-list';
8
+ import type { Session, SessionEvent, SessionHeader, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
9
+ import type { SubagentCatalogEntry } from './projection-types.ts';
10
+ /** Current payload version for `subagent/catalog` events. */
11
+ export declare const SUBAGENT_CATALOG_VERSION = 0;
12
+ /** One complete parent-owned catalog fact. */
13
+ export type SubagentCatalogEvent = {
14
+ readonly version: 0;
15
+ readonly childId: SessionId;
16
+ readonly childCreatedAt: number;
17
+ } & ({
18
+ readonly mode: 'one-shot';
19
+ readonly label?: string;
20
+ } | {
21
+ readonly mode: 'continuable';
22
+ readonly label: string;
23
+ });
24
+ declare module '@deepseek-ai/dsh-session/types' {
25
+ interface SessionEventMap {
26
+ /**
27
+ * A direct child's complete discovery fact.
28
+ * @param data - versioned parent-owned catalog entry.
29
+ */
30
+ 'subagent/catalog': SubagentCatalogEvent;
31
+ }
32
+ }
33
+ /** Host fold state for one parent catalog. */
34
+ export interface SubagentCatalogState {
35
+ readonly inheritedEventCount: SessionLogOffset;
36
+ readonly head?: ChunkedList<SubagentCatalogEvent> | undefined;
37
+ }
38
+ declare module '@deepseek-ai/dsh-session-projection/types' {
39
+ interface SessionProjectionStateMap {
40
+ subagentCatalog: SubagentCatalogState;
41
+ }
42
+ }
43
+ /**
44
+ * Materialize direct children from their parent's successful creation facts.
45
+ * @param state - parent catalog fold state.
46
+ * @returns current direct-child rows in parent catalog event order.
47
+ */
48
+ declare function subagentCatalogEntries(state: SubagentCatalogState): SubagentCatalogEntry[];
49
+ /** Parent-owned direct-child catalog projection; invalid own facts reject restoration. */
50
+ export declare const subagentCatalogProjectionDefinition: {
51
+ key: "subagentCatalog";
52
+ stateSchema: z.ZodType<SubagentCatalogState, unknown, z.core.$ZodTypeInternals<SubagentCatalogState, unknown>>;
53
+ init: (_header: SessionHeader, inheritedEventCount: SessionLogOffset) => {
54
+ inheritedEventCount: SessionLogOffset;
55
+ };
56
+ apply: (state: NoInfer<SubagentCatalogState>, event: SessionEvent) => SubagentCatalogState;
57
+ stateVersion: number;
58
+ wire: {
59
+ viewSchema: z.ZodType<SubagentCatalogEntry[], unknown, z.core.$ZodTypeInternals<SubagentCatalogEntry[], unknown>>;
60
+ view: typeof subagentCatalogEntries;
61
+ };
62
+ };
63
+ /**
64
+ * Append a complete direct-child discovery fact to its parent Session.
65
+ * @param parent - durable direct parent receiving the discovery fact.
66
+ * @param child - established child's immutable Session metadata.
67
+ * @param descriptor - mode-discriminated creation label frozen with the child.
68
+ */
69
+ export declare function establishCatalogChild(parent: Session, child: SessionHeader, descriptor: {
70
+ readonly mode: 'one-shot';
71
+ readonly label?: string;
72
+ } | {
73
+ readonly mode: 'continuable';
74
+ readonly label: string;
75
+ }): void;
76
+ export {};
77
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Parent-owned durable subagent catalog events and their chunked projection.
3
+ *
4
+ * @module @deepseek-ai/dsh-subagent/catalog
5
+ */
6
+ import { z } from 'zod';
7
+ import { appendChunkedList, chunkedListSchema, iterateChunkedList } from '@deepseek-ai/dsh-chunked-list';
8
+ /** Current payload version for `subagent/catalog` events. */
9
+ export const SUBAGENT_CATALOG_VERSION = 0;
10
+ const sessionIdSchema = z.string();
11
+ const oneShotCatalogSchema = z.object({
12
+ version: z.literal(SUBAGENT_CATALOG_VERSION),
13
+ childId: sessionIdSchema,
14
+ childCreatedAt: z.number().int().nonnegative(),
15
+ mode: z.literal('one-shot'),
16
+ label: z.string().optional(),
17
+ }).strict();
18
+ const continuableCatalogSchema = z.object({
19
+ version: z.literal(SUBAGENT_CATALOG_VERSION),
20
+ childId: sessionIdSchema,
21
+ childCreatedAt: z.number().int().nonnegative(),
22
+ mode: z.literal('continuable'),
23
+ label: z.string(),
24
+ }).strict();
25
+ const eventDataSchema = z.union([
26
+ oneShotCatalogSchema,
27
+ continuableCatalogSchema,
28
+ ]);
29
+ const viewSchema = z.array(z.union([
30
+ oneShotCatalogSchema.omit({ version: true, childId: true, childCreatedAt: true }).extend({
31
+ id: sessionIdSchema,
32
+ createdAt: oneShotCatalogSchema.shape.childCreatedAt,
33
+ }),
34
+ continuableCatalogSchema.omit({ version: true, childId: true, childCreatedAt: true }).extend({
35
+ id: sessionIdSchema,
36
+ createdAt: continuableCatalogSchema.shape.childCreatedAt,
37
+ }),
38
+ ]));
39
+ const stateSchema = z.object({
40
+ inheritedEventCount: z.number().int().nonnegative(),
41
+ head: chunkedListSchema(eventDataSchema).optional(),
42
+ }).strict();
43
+ /**
44
+ * Materialize direct children from their parent's successful creation facts.
45
+ * @param state - parent catalog fold state.
46
+ * @returns current direct-child rows in parent catalog event order.
47
+ */
48
+ function subagentCatalogEntries(state) {
49
+ const entries = [];
50
+ for (const data of iterateChunkedList(state.head)) {
51
+ entries.push(data.mode === 'one-shot'
52
+ ? {
53
+ id: data.childId,
54
+ createdAt: data.childCreatedAt,
55
+ mode: data.mode,
56
+ ...data.label === undefined ? {} : { label: data.label },
57
+ }
58
+ : {
59
+ id: data.childId,
60
+ createdAt: data.childCreatedAt,
61
+ mode: data.mode,
62
+ label: data.label,
63
+ });
64
+ }
65
+ return entries;
66
+ }
67
+ /** Parent-owned direct-child catalog projection; invalid own facts reject restoration. */
68
+ export const subagentCatalogProjectionDefinition = {
69
+ key: 'subagentCatalog',
70
+ stateSchema,
71
+ init: (_header, inheritedEventCount) => ({ inheritedEventCount }),
72
+ apply: (state, event) => {
73
+ if (event.type !== 'subagent/catalog' || event.seq < state.inheritedEventCount)
74
+ return state;
75
+ return { ...state, head: appendChunkedList(state.head, eventDataSchema.parse(event.data)) };
76
+ },
77
+ stateVersion: 2,
78
+ wire: { viewSchema, view: subagentCatalogEntries },
79
+ };
80
+ /**
81
+ * Append a complete direct-child discovery fact to its parent Session.
82
+ * @param parent - durable direct parent receiving the discovery fact.
83
+ * @param child - established child's immutable Session metadata.
84
+ * @param descriptor - mode-discriminated creation label frozen with the child.
85
+ */
86
+ export function establishCatalogChild(parent, child, descriptor) {
87
+ parent.append('subagent/catalog', descriptor.mode === 'one-shot'
88
+ ? {
89
+ version: SUBAGENT_CATALOG_VERSION,
90
+ childId: child.id,
91
+ childCreatedAt: child.createdAt,
92
+ mode: descriptor.mode,
93
+ ...descriptor.label === undefined ? {} : { label: descriptor.label },
94
+ }
95
+ : {
96
+ version: SUBAGENT_CATALOG_VERSION,
97
+ childId: child.id,
98
+ childCreatedAt: child.createdAt,
99
+ mode: descriptor.mode,
100
+ label: descriptor.label,
101
+ });
102
+ }
103
+ //# sourceMappingURL=catalog.js.map
@@ -4,5 +4,5 @@
4
4
  * @module @deepseek-ai/dsh-subagent/client
5
5
  */
6
6
  export type * from './control-types.ts';
7
- export type { SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts';
7
+ export type { SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts';
8
8
  //# sourceMappingURL=client.d.ts.map
@@ -337,7 +337,6 @@ export class ContinuableActivationRegistry {
337
337
  finally {
338
338
  this.wake(activation);
339
339
  }
340
- activation.announced = true;
341
340
  return message.id;
342
341
  }
343
342
  /**
@@ -405,8 +404,7 @@ export class ContinuableActivationRegistry {
405
404
  async materializeTracked(inputs, parentLineage) {
406
405
  const { childId, provider, parent, create } = inputs;
407
406
  inputs.signal.throwIfAborted();
408
- const setup = (childCtx) => {
409
- const child = childCtx.agent;
407
+ const setup = (childCtx, child) => {
410
408
  // Only fresh creation appends the descriptor and delegated policy after
411
409
  // the inherited marker; a cold resume replays those persisted events.
412
410
  if (create !== undefined) {
@@ -419,12 +417,14 @@ export class ContinuableActivationRegistry {
419
417
  const handle = create === undefined
420
418
  ? await this.ownerCtx.agents.resume({
421
419
  resumeSessionId: childId,
420
+ parentAgent: parent,
422
421
  agentOptions: inputs.agentOptions,
423
422
  signal: inputs.signal,
424
423
  setup,
425
424
  })
426
425
  : await this.ownerCtx.agents.create({
427
426
  sessionId: childId,
427
+ parentAgent: parent,
428
428
  meta: create.meta,
429
429
  ...(create.seed === undefined ? {} : { seed: create.seed }),
430
430
  inheritedEventCount: create.inheritedEventCount,
@@ -109,7 +109,7 @@ export declare class SubagentContinuationManager {
109
109
  * supplies every reconstruction input; no subagent provider is dispatched.
110
110
  */
111
111
  private coldResume;
112
- /** Submit to a freshly materialized Activation or roll it back completely. */
112
+ /** Admit a materialized child, commit its creation fact, and release it on failure. */
113
113
  private submitMaterialized;
114
114
  /** Build and submit one message across the final synchronous admission cutoff. */
115
115
  private submitAdmitted;
@@ -73,6 +73,7 @@ import { ContinuableActivationRegistry, } from "./continuation-activation.js";
73
73
  import { createAgentMessage, withContinuableReturnGuidance, } from "./continuation-messages.js";
74
74
  import { assertSubagentMaxDepth } from "./depth.js";
75
75
  import { foldSubagentDescriptor, snapshotSubagentDescriptor } from "./descriptor.js";
76
+ import { establishCatalogChild } from "./catalog.js";
76
77
  import { SubagentError } from "./error.js";
77
78
  import { isAdjacentAgentSendMessageTool } from "./internal.js";
78
79
  /**
@@ -167,9 +168,10 @@ export class SubagentContinuationManager {
167
168
  composition: { persona: request.persona, toolFilter: request.toolFilter },
168
169
  signal: spec.signal,
169
170
  });
170
- return this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get('tools')?.get('send_message', activation.handle.agent))
171
+ const childHeader = activation.handle.agent.session.header;
172
+ return await this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get('tools')?.get('send_message', activation.handle.agent))
171
173
  ? withContinuableReturnGuidance(parent.id, request.prompt)
172
- : request.prompt, { source: { kind: 'user' }, signal: spec.signal, delivery: 'queue' }, parent);
174
+ : request.prompt, { source: { kind: 'user' }, signal: spec.signal, delivery: 'queue' }, parent, () => { establishCatalogChild(parent.session, childHeader, descriptor); });
173
175
  });
174
176
  return { childId, messageId };
175
177
  }
@@ -264,7 +266,9 @@ export class SubagentContinuationManager {
264
266
  return undefined;
265
267
  }
266
268
  }
267
- return this.submitAdmitted(activation, content, options, parent);
269
+ const messageId = this.submitAdmitted(activation, content, options, parent);
270
+ activation.announced = true;
271
+ return messageId;
268
272
  });
269
273
  /* v8 ignore start -- only a delivery that lost the disposal cutoff retries. */
270
274
  if (live !== undefined)
@@ -385,8 +389,8 @@ export class SubagentContinuationManager {
385
389
  __disposeResources(env_1);
386
390
  }
387
391
  }
388
- /** Submit to a freshly materialized Activation or roll it back completely. */
389
- async submitMaterialized(activation, content, options, parent) {
392
+ /** Admit a materialized child, commit its creation fact, and release it on failure. */
393
+ async submitMaterialized(activation, content, options, parent, commit) {
390
394
  try {
391
395
  if (contentHasImage(content)) {
392
396
  await this.assertImageCapable(activation.handle.agent, options.signal);
@@ -394,12 +398,18 @@ export class SubagentContinuationManager {
394
398
  throw new SubagentError(`subagent "${activation.childId}" is closing`, 'ACTIVATION_CLOSING');
395
399
  }
396
400
  }
397
- return this.submitAdmitted(activation, content, options, parent);
401
+ const messageId = this.submitAdmitted(activation, content, options, parent);
402
+ commit?.();
403
+ activation.announced = true;
404
+ return messageId;
398
405
  }
399
406
  catch (error) {
400
- /* v8 ignore next -- rollback disposal failures must not mask the
401
- * pre-acceptance signal, drain, or lifecycle failure. */
402
- await this.activations.dispose(activation).catch(() => undefined);
407
+ try {
408
+ await this.activations.dispose(activation);
409
+ }
410
+ catch (cleanupError) {
411
+ this.ctx.logger.warn(`subagent continuation: disposal after admission or catalog append failure also failed: ${String(cleanupError)}`);
412
+ }
403
413
  throw error;
404
414
  }
405
415
  }
@@ -27,7 +27,7 @@ export class SubagentInbox {
27
27
  * @returns whether either Agent inbox destination is non-empty.
28
28
  */
29
29
  get hasPending() {
30
- return this.agent.inbox.hasPending;
30
+ return this.agent.inbox.nextTurn.length > 0 || this.agent.inbox.nextStep.length > 0;
31
31
  }
32
32
  /**
33
33
  * Submit through the Agent only while its Activation remains resident.
@@ -38,6 +38,7 @@ import type { SubagentCatalog, SubagentInterruptReceipt, SubagentPromptReceipt,
38
38
  import type { ContinuableStart, ContinuableStartSpec, SubagentInterruptAuthority, SubagentProvider, SubagentRun, SubagentRunEndInfo, SubagentRunInfo, SubagentSendMessageOptions, SubagentStartRequest } from './types.ts';
39
39
  import type { SubagentDescendantListEntry, SubagentListEntry } from './list-children.ts';
40
40
  import { deliverSubagentPrompt } from './internal.ts';
41
+ export type {} from './catalog.ts';
41
42
  export * from './out-of-process.ts';
42
43
  export { AssistantOutputFold, finalAssistantOutput } from './assistant-output.ts';
43
44
  export { SubagentRunId } from './types.ts';
@@ -286,6 +287,8 @@ export declare class SubagentRuntime extends TypertRemoteService {
286
287
  * fulfills; a rejection therefore has no run for the caller to dispose and
287
288
  * emits no run lifecycle events. Post-publication turn and infrastructure
288
289
  * failures settle through the returned run.
290
+ * A catalog append failure disposes the run and handles its result rejection;
291
+ * the caller receives the catalog error even if disposal also fails.
289
292
  * @param name - the provider to use.
290
293
  * @param request - child label, prompt, parent, signal, and optional capabilities.
291
294
  * @returns the published holder-owned run.
@@ -74,6 +74,7 @@ import SubagentContinuationManager from "./continuation.js";
74
74
  import { listChildren as listSubagentChildren, listDescendants as listSubagentDescendants } from "./list-children.js";
75
75
  import { snapshotSubagentDescriptor } from "./descriptor.js";
76
76
  import { subagentIdentityProjectionDefinition, subagentTimingProjectionDefinition } from "./projection.js";
77
+ import { establishCatalogChild, subagentCatalogProjectionDefinition } from "./catalog.js";
77
78
  import { deliverSubagentPrompt } from "./internal.js";
78
79
  export * from "./out-of-process.js";
79
80
  export { AssistantOutputFold, finalAssistantOutput } from "./assistant-output.js";
@@ -125,6 +126,7 @@ let SubagentRuntime = (() => {
125
126
  }, 'subagents.continuationBinding()');
126
127
  });
127
128
  ctx.inject(['sessionProjections'], (projectionCtx) => {
129
+ projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
128
130
  projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
129
131
  projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
130
132
  });
@@ -414,6 +416,8 @@ let SubagentRuntime = (() => {
414
416
  * fulfills; a rejection therefore has no run for the caller to dispose and
415
417
  * emits no run lifecycle events. Post-publication turn and infrastructure
416
418
  * failures settle through the returned run.
419
+ * A catalog append failure disposes the run and handles its result rejection;
420
+ * the caller receives the catalog error even if disposal also fails.
417
421
  * @param name - the provider to use.
418
422
  * @param request - child label, prompt, parent, signal, and optional capabilities.
419
423
  * @returns the published holder-owned run.
@@ -430,7 +434,25 @@ let SubagentRuntime = (() => {
430
434
  ...request.label !== undefined ? { label: request.label } : {},
431
435
  });
432
436
  const resolved = { ...request, descriptor };
433
- return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
437
+ const run = await provider.start(resolved);
438
+ const child = run.localAgent?.session;
439
+ if (child !== undefined) {
440
+ try {
441
+ establishCatalogChild(request.parent.session, child.header, descriptor);
442
+ }
443
+ catch (error) {
444
+ // No caller receives this run; the catalog error owns the failed start.
445
+ void run.result.catch(() => undefined);
446
+ try {
447
+ await run.dispose();
448
+ }
449
+ catch (cleanupError) {
450
+ this.ctx.logger.warn(`subagent: disposal after catalog append failure also failed: ${String(cleanupError)}`);
451
+ }
452
+ throw error;
453
+ }
454
+ }
455
+ return observeRun(this.emitLifecycle, name, request.parent, run);
434
456
  }
435
457
  /**
436
458
  * Resolve one provider's detached continuable-creation contribution. Method
@@ -3,7 +3,18 @@
3
3
  *
4
4
  * @module @deepseek-ai/dsh-subagent/projection-types
5
5
  */
6
- import type { SessionSeq } from '@deepseek-ai/dsh-session/types';
6
+ import type { SessionId, SessionSeq } from '@deepseek-ai/dsh-session/types';
7
+ /** One current direct-child discovery row materialized from parent facts. */
8
+ export type SubagentCatalogEntry = {
9
+ readonly id: SessionId;
10
+ readonly createdAt: number;
11
+ } & ({
12
+ readonly mode: 'one-shot';
13
+ readonly label?: string;
14
+ } | {
15
+ readonly mode: 'continuable';
16
+ readonly label: string;
17
+ });
7
18
  /** Durable active-turn timing for one descriptor-backed child session. */
8
19
  export interface SubagentTimingProjection {
9
20
  /** Milliseconds accumulated across completed turns after the child's own descriptor. */
@@ -44,6 +55,8 @@ export type SubagentIdentityProjection = {
44
55
  };
45
56
  declare module '@deepseek-ai/dsh-session-projection/types' {
46
57
  interface SessionProjectionMap {
58
+ /** Direct children in parent catalog event order, excluding fork-inherited facts. */
59
+ subagentCatalog: SubagentCatalogEntry[];
47
60
  /** Active-turn duration for a descriptor-backed subagent session. */
48
61
  subagentTiming: SubagentTimingProjection;
49
62
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-subagent",
3
3
  "description": "Abstract subagent seam (ctx.subagents): named-provider registry for delegating to child agents",
4
- "version": "0.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -54,30 +54,31 @@
54
54
  "license": "MIT",
55
55
  "dependencies": {
56
56
  "zod": "^4.4.3",
57
- "@deepseek-ai/dsh-brand": "^0.1.3-alpha.2",
58
- "@deepseek-ai/dsh-util-values": "^0.1.3-alpha.2"
57
+ "@deepseek-ai/dsh-brand": "^0.1.5-alpha.2",
58
+ "@deepseek-ai/dsh-util-values": "^0.1.5-alpha.2",
59
+ "@deepseek-ai/dsh-chunked-list": "^0.1.5-alpha.2"
59
60
  },
60
61
  "peerDependencies": {
61
62
  "@deepseek-ai/cordis": "^4.0.2",
62
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
63
- "@deepseek-ai/dsh-agent-presets": "^0.1.3-alpha.2",
64
- "@deepseek-ai/dsh-attachment": "^0.1.3-alpha.2",
65
- "@deepseek-ai/dsh-invariants": "^0.1.3-alpha.2",
66
- "@deepseek-ai/dsh-jobs": "^0.1.3-alpha.2",
67
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
68
- "@deepseek-ai/dsh-sandbox": "^0.1.3-alpha.2",
69
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.3-alpha.2",
70
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
71
- "@deepseek-ai/dsh-session-persistence": "^0.1.3-alpha.2",
72
- "@deepseek-ai/dsh-session-projection": "^0.1.3-alpha.2",
73
- "@deepseek-ai/dsh-session-projection-cache": "^0.1.3-alpha.2",
74
- "@deepseek-ai/dsh-session-query": "^0.1.3-alpha.2",
75
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
76
- "@deepseek-ai/dsh-typert-protocol": "^0.1.3-alpha.2",
77
- "@deepseek-ai/dsh-scope": "^0.1.3-alpha.2",
78
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
79
- "@deepseek-ai/dsh-user-approval": "^0.1.3-alpha.2",
80
- "@deepseek-ai/dsh-util-time": "^0.1.3-alpha.2"
63
+ "@deepseek-ai/dsh-agent-presets": "^0.1.5-alpha.2",
64
+ "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2",
65
+ "@deepseek-ai/dsh-invariants": "^0.1.5-alpha.2",
66
+ "@deepseek-ai/dsh-jobs": "^0.1.5-alpha.2",
67
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
68
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
69
+ "@deepseek-ai/dsh-sandbox": "^0.1.5-alpha.2",
70
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.5-alpha.2",
71
+ "@deepseek-ai/dsh-scope": "^0.1.5-alpha.2",
72
+ "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.2",
73
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
74
+ "@deepseek-ai/dsh-session-projection": "^0.1.5-alpha.2",
75
+ "@deepseek-ai/dsh-session-projection-cache": "^0.1.5-alpha.2",
76
+ "@deepseek-ai/dsh-session-query": "^0.1.5-alpha.2",
77
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
78
+ "@deepseek-ai/dsh-util-time": "^0.1.5-alpha.2",
79
+ "@deepseek-ai/dsh-user-approval": "^0.1.5-alpha.2",
80
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2",
81
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.2"
81
82
  },
82
83
  "peerDependenciesMeta": {
83
84
  "@deepseek-ai/dsh-agent-presets": {
@@ -110,27 +111,28 @@
110
111
  },
111
112
  "devDependencies": {
112
113
  "@deepseek-ai/cordis": "^4.0.2",
113
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
114
- "@deepseek-ai/dsh-agent-presets": "^0.1.3-alpha.2",
115
- "@deepseek-ai/dsh-attachment": "^0.1.3-alpha.2",
116
- "@deepseek-ai/dsh-invariants": "^0.1.3-alpha.2",
117
- "@deepseek-ai/dsh-jobs": "^0.1.3-alpha.2",
118
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
119
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.3-alpha.2",
120
- "@deepseek-ai/dsh-sandbox": "^0.1.3-alpha.2",
121
- "@deepseek-ai/dsh-scope": "^0.1.3-alpha.2",
122
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
123
- "@deepseek-ai/dsh-session-persistence": "^0.1.3-alpha.2",
124
- "@deepseek-ai/dsh-session-projection": "^0.1.3-alpha.2",
125
- "@deepseek-ai/dsh-session-query": "^0.1.3-alpha.2",
126
- "@deepseek-ai/dsh-storage": "^0.1.3-alpha.2",
127
- "@deepseek-ai/dsh-session-projection-cache": "^0.1.3-alpha.2",
128
- "@deepseek-ai/dsh-storage-json": "^0.1.3-alpha.2",
129
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
130
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
131
- "@deepseek-ai/dsh-typert-protocol": "^0.1.3-alpha.2",
132
- "@deepseek-ai/dsh-user-approval": "^0.1.3-alpha.2",
133
- "@deepseek-ai/dsh-storage-domain": "^0.1.3-alpha.2",
134
- "@deepseek-ai/dsh-util-time": "^0.1.3-alpha.2"
114
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
115
+ "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2",
116
+ "@deepseek-ai/dsh-invariants": "^0.1.5-alpha.2",
117
+ "@deepseek-ai/dsh-jobs": "^0.1.5-alpha.2",
118
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
119
+ "@deepseek-ai/dsh-sandbox": "^0.1.5-alpha.2",
120
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.5-alpha.2",
121
+ "@deepseek-ai/dsh-schedule": "^0.1.5-alpha.2",
122
+ "@deepseek-ai/dsh-scope": "^0.1.5-alpha.2",
123
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
124
+ "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.2",
125
+ "@deepseek-ai/dsh-session-projection": "^0.1.5-alpha.2",
126
+ "@deepseek-ai/dsh-session-projection-cache": "^0.1.5-alpha.2",
127
+ "@deepseek-ai/dsh-storage": "^0.1.5-alpha.2",
128
+ "@deepseek-ai/dsh-storage-domain": "^0.1.5-alpha.2",
129
+ "@deepseek-ai/dsh-storage-json": "^0.1.5-alpha.2",
130
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
131
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
132
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2",
133
+ "@deepseek-ai/dsh-user-approval": "^0.1.5-alpha.2",
134
+ "@deepseek-ai/dsh-util-time": "^0.1.5-alpha.2",
135
+ "@deepseek-ai/dsh-agent-presets": "^0.1.5-alpha.2",
136
+ "@deepseek-ai/dsh-session-query": "^0.1.5-alpha.2"
135
137
  }
136
138
  }