@deepseek-ai/dsh-subagent 0.1.0-rc.8 → 0.1.1-rc.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 +1 -1
- package/README.zh.md +9 -5
- package/lib/index.js +39 -13
- package/lib/types/projection.d.ts +55 -8
- package/lib/types/projection.js +30 -15
- package/package.json +35 -35
package/README.i18n.yaml
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
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
5
|
README.md: e84a6b486253e81ccf7e7df12c4149e6df4ed9f2
|
|
6
|
-
README.zh.md:
|
|
6
|
+
README.zh.md: e289863531c1686cedeccadfa76e2661dfa9bfc8
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
subagent seam 允许一个 agent(智能体)通过具名提供方把工作委派给子 agent。调用方统一使用 `ctx.subagents` 服务 API;提供方决定子 agent 在当前进程、其他进程,还是通过未来的传输方式运行。
|
|
6
6
|
|
|
7
|
-
[subagent 家族概述](../README.md)列出了实现和面向模型的消费方。本包负责提供方注册表、共享请求和结果约定、持久描述符以及可继续子级编排。多个具名提供方可以在该约定背后共存。
|
|
7
|
+
[subagent 家族概述](../README.zh.md)列出了实现和面向模型的消费方。本包负责提供方注册表、共享请求和结果约定、持久描述符以及可继续子级编排。多个具名提供方可以在该约定背后共存。
|
|
8
8
|
|
|
9
9
|
## 服务 API
|
|
10
10
|
|
|
@@ -41,7 +41,7 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委
|
|
|
41
41
|
- `toolFilter`:应用请求的子 agent 工具限制;
|
|
42
42
|
- `persona`:应用每个子 agent 独立的 persona。
|
|
43
43
|
|
|
44
|
-
每个进程内子 agent 都通过一次 `applyChildComposition(childCtx, parent, composition)` 调用完成组装:先加入父级的 agent-preset 组合,再应用子 agent 自己的 persona 和工具限制。加入父级组合正是子 agent 获得能力的途径:所有面向模型的行都位于 agent 平面,完全没有加入任何组合的子 agent 抵达模型时会看到空的工具注册表(见 [`dsh-agent-presets`](../../preset/agent-presets/README.md))。将父级作为参数是刻意设计:这让“组装子 agent 却不做该加入”在各调用点无法表达,而这正是这一次调用所要杜绝的缺陷。未组装 preset roster 的部署不加入任何组合、也不需要加入;其面向模型的行位于宿主组合中,子 agent 已能通过工具注册表的全局层解析到它们。
|
|
44
|
+
每个进程内子 agent 都通过一次 `applyChildComposition(childCtx, parent, composition)` 调用完成组装:先加入父级的 agent-preset 组合,再应用子 agent 自己的 persona 和工具限制。加入父级组合正是子 agent 获得能力的途径:所有面向模型的行都位于 agent 平面,完全没有加入任何组合的子 agent 抵达模型时会看到空的工具注册表(见 [`dsh-agent-presets`](../../preset/agent-presets/README.zh.md))。将父级作为参数是刻意设计:这让“组装子 agent 却不做该加入”在各调用点无法表达,而这正是这一次调用所要杜绝的缺陷。未组装 preset roster 的部署不加入任何组合、也不需要加入;其面向模型的行位于宿主组合中,子 agent 已能通过工具注册表的全局层解析到它们。
|
|
45
45
|
|
|
46
46
|
`childSessionMeta()` 把所加入的 preset id 记在子 agent 的持久化 header 上,理由与顶层会话记录自己的那一个相同:preset 决定了模型所见的工具 schema 与提示段,因此冷读子 agent 的历史时必须重建那份组装,而不是部署默认值。该值从父方**活着的** scope 链读取,而不是从父方 header 读取,因为在空白期切换过 preset 的父方运行在更新的那份组装上,而它的 header 仍写着旧的那个。
|
|
47
47
|
|
|
@@ -57,15 +57,17 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委
|
|
|
57
57
|
|
|
58
58
|
`inheritsParentContext` 只用于描述,不能强制执行。它仅说明子 agent 是否能看到父级已完成的对话历史(`fork` 可以;`spawn` 和各进程外一次性提供方不可以),不表示是否继承工具、服务或权限。
|
|
59
59
|
|
|
60
|
+
<a id="delegated-policy"></a>
|
|
61
|
+
|
|
60
62
|
## 委派策略
|
|
61
63
|
|
|
62
|
-
两条进程内委派路径都会通过共享的子 agent 辅助函数,在委派边界固定子 agent 的权限范围。`captureDelegatedPolicyOverrides(parent)` 会为父会话的显式沙箱覆盖项(`sandboxPolicy.overrideOf()`)创建快照,并在审批能力已组合时将子 agent 的审批策略固定为 `'never'`,无论父级自身采用何种策略。这样,被委派的子 agent 只能在继承的沙箱范围内行动,每次审批请求(例如 `sandbox_permissions` 升权)都会被确定性拒绝,而不会等待无人处理的提示(这两个服务都是可选的 `ctx.get` 消费方)。`appendDelegatedPolicyOverrides()` 则在未发布的设置阶段、在任何 fork 种子之后,把每个值作为一条 `source: 'delegation'` 的 `sandbox/mode` 或 `approval/policy` 事件写入子 agent 自己的日志。因此,新捕获的策略会覆盖种子中的陈旧状态,而子 agent 的生效策略始终可以仅凭其日志重建。沙箱的部署默认值绝不复制:未切换的父级不会记录 `sandbox/mode`,其子 agent 会动态跟随部署默认值。可继续启动会在第一次 await 前捕获策略,并且只为全新物化写入这些委派事件;冷恢复只会重放已持久化的委派事件,不会重新捕获父级策略,因此创建之后的父级切换绝不会追溯性地改变持久化子 agent。每个进程内子 agent 还会收到一条作用域内的运行时上下文声明(`subagent:delegation`),告知其权限范围已固定,需要更宽访问的任务应以上报限制收尾,而不是重试。参见[一次性](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)与[可继续](../../../.agents/notes/implemented/feature/2026-08-10-continuable-subagent-policy-inheritance.md)两篇委派策略 Agent Note。
|
|
64
|
+
两条进程内委派路径都会通过共享的子 agent 辅助函数,在委派边界固定子 agent 的权限范围。`captureDelegatedPolicyOverrides(parent)` 会为父会话的显式沙箱覆盖项(`sandboxPolicy.overrideOf()`)创建快照,并在审批能力已组合时将子 agent 的审批策略固定为 `'never'`,无论父级自身采用何种策略。这样,被委派的子 agent 只能在继承的沙箱范围内行动,每次审批请求(例如 `sandbox_permissions` 升权)都会被确定性拒绝,而不会等待无人处理的提示(这两个服务都是可选的 `ctx.get` 消费方)。`appendDelegatedPolicyOverrides()` 则在未发布的设置阶段、在任何 fork 种子之后,把每个值作为一条 `source: 'delegation'` 的 `sandbox/mode` 或 `approval/policy` 事件写入子 agent 自己的日志。因此,新捕获的策略会覆盖种子中的陈旧状态,而子 agent 的生效策略始终可以仅凭其日志重建。沙箱的部署默认值绝不复制:未切换的父级不会记录 `sandbox/mode`,其子 agent 会动态跟随部署默认值。可继续启动会在第一次 await 前捕获策略,并且只为全新物化写入这些委派事件;冷恢复只会重放已持久化的委派事件,不会重新捕获父级策略,因此创建之后的父级切换绝不会追溯性地改变持久化子 agent。每个进程内子 agent 还会收到一条作用域内的运行时上下文声明(`subagent:delegation`),告知其权限范围已固定,需要更宽访问的任务应以上报限制收尾,而不是重试。参见[一次性](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.zh.md)与[可继续](../../../.agents/notes/implemented/feature/2026-08-10-continuable-subagent-policy-inheritance.zh.md)两篇委派策略 Agent Note。
|
|
63
65
|
|
|
64
66
|
## 一次性所有权与生命周期
|
|
65
67
|
|
|
66
68
|
`provider.start(request): Promise<SubagentRun>` 是所有权转移边界;委派工具也会在其由 Task 支撑的一次性后台路径中使用它。兑现前,提供方拥有设置过程,并且在任何失败路径上都必须取消、回滚并使尚未发布的资源完全停稳。兑现后,run 的所有权转移给调用方;调用方必须在每条路径上调用 `dispose()`。剩余提示词和轮次工作属于 `SubagentRun.result`。
|
|
67
69
|
|
|
68
|
-
`SubagentRun.result` 兑现为 `{ output, structured?, diagnostic?, stopReason }`。子 agent 级失败会以非 `completed` 原因兑现;只有 seam 无法表示的基础设施故障才可以拒绝。提供方可以为非完成结果附加安全的 `diagnostic`:它会先排除工具输入、文件内容、环境值、凭证与原始协议载荷,并把完整文本限制在 4096 个 UTF-8 字节以内。共享结果类型不定义提供方类别或生命周期阶段:进程外提供方可以从锁定版本产品提供的结构化事实与已观测的进程结果派生固定展示文本,而消费方只负责原样呈现,不解析该文本。该字段不是 assistant 输出;消费方会将它分开呈现,它也不会进入 `subagent/end.lastAssistantMessage`。`dispose()` 是幂等的,会取消剩余工作,并等待结果结算以及子 agent 资源完全停稳。result 的拒绝只通过 `result` 本身报告;只有独立的资源释放失败,才会使 `dispose()` 被拒绝。`output` 与 `subagent/end` 事件的 `lastAssistantMessage` 使用导出的 `AssistantOutputFold`/`finalAssistantOutput` 辅助函数选取子 agent 最后一条非空 assistant 消息;若没有这类消息,则选取其累积的 assistant 文本。子 agent 两种输出均未产生时,`output` 为 `[]`,该事件字段缺省(终态结果约定归 [`SubagentResult`](../../../docs/subsystems/subagent.md#the-terminal-result-subagentresult) 所有)。
|
|
70
|
+
`SubagentRun.result` 兑现为 `{ output, structured?, diagnostic?, stopReason }`。子 agent 级失败会以非 `completed` 原因兑现;只有 seam 无法表示的基础设施故障才可以拒绝。提供方可以为非完成结果附加安全的 `diagnostic`:它会先排除工具输入、文件内容、环境值、凭证与原始协议载荷,并把完整文本限制在 4096 个 UTF-8 字节以内。共享结果类型不定义提供方类别或生命周期阶段:进程外提供方可以从锁定版本产品提供的结构化事实与已观测的进程结果派生固定展示文本,而消费方只负责原样呈现,不解析该文本。该字段不是 assistant 输出;消费方会将它分开呈现,它也不会进入 `subagent/end.lastAssistantMessage`。`dispose()` 是幂等的,会取消剩余工作,并等待结果结算以及子 agent 资源完全停稳。result 的拒绝只通过 `result` 本身报告;只有独立的资源释放失败,才会使 `dispose()` 被拒绝。`output` 与 `subagent/end` 事件的 `lastAssistantMessage` 使用导出的 `AssistantOutputFold`/`finalAssistantOutput` 辅助函数选取子 agent 最后一条非空 assistant 消息;若没有这类消息,则选取其累积的 assistant 文本。子 agent 两种输出均未产生时,`output` 为 `[]`,该事件字段缺省(终态结果约定归 [`SubagentResult`](../../../docs/subsystems/subagent.zh.md#the-terminal-result-subagentresult) 所有)。
|
|
69
71
|
|
|
70
72
|
本地运行会在 `start()` 兑现前发布普通的子 agent/会话,把该共享会话 id 作为 `SubagentRun.id` 返回,以 `SubagentRun.localAgent` 公开准确的子 agent,把 `request.parent.session.id` 记录到子 agent 的 `parentSession` header,并在其初始轮次内追加已解析的描述符。远程提供方则生成 parent 作用域的生命周期 id,并返回 `localAgent: undefined`;由于没有本地 child 会话,其一次性运行不会进入基于追踪的枚举结果。
|
|
71
73
|
|
|
@@ -103,12 +105,14 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委
|
|
|
103
105
|
|
|
104
106
|
## 收集模型
|
|
105
107
|
|
|
106
|
-
面向模型的工具默认同步收集:先等待子 agent 结果,再 dispose 运行,然后才返回。一次性后台委派会在工具中注册普通 Task,其通用状态、收集和取消工具负责后续交互,并将模型提供的 `description` 持久化为可选显示标签。可继续后台委派会调用 `ctx.subagents.startContinuable()`,只返回持久化子 agent id;子 agent 自 inbox 接受起就拥有自己的轮次,因此没有 Task、也没有结果 promise——调用方通过 `send_message` 后续操作工具发送后续工作,`interrupt()` 只停止当前轮次而不 dispose 子 agent,而持久化子 agent 会话仍是子 agent 详细输出的来源。只有 `ctx.agents` 可用时,继续执行管理器才会存在,而会话持久化按每项继续执行操作解析。与此独立,`listChildren()` 枚举在线会话存储与可选会话持久化的在线优先合并——持久化缺席时仅枚举在线 child,因为那时冷 child 本就无法恢复——并由已注册的 `subagent` 投影单元供给每个 child 的持久化模式与标签:在线 child 取注册表的水位快照;冷 child 先取可选投影缓存的持久化行,且仅当其 `seq` 门证明该值折叠自 child 自身后缀(fork 种子之后——自有描述符一经追加即不可变)才直接采用,否则经一次有界并发的持久化 inspect 再经注册表折叠,且 inspect 结果必须仍指向枚举时的生命周期(同 id 被重新发布的会话降级为 `corrupt` diagnostic)。缓存读取抛出异常时,不会据此作出分类判断,因为缓存只是派生数据;静默落到该权威重折。分类结果完全以投影折叠为准;列表操作本身不解析描述符。取得身份值即产出 child 行;已定局而折叠未产出身份的候选是 `corrupt` diagnostic,inspect 失败是瞬时的 `unavailable`(下次列表重试),运行中而暂无身份值的候选整行省略(描述符尚未追加的创建窗口)。它不查询继续执行管理器、Agent 注册信息、Activation 或提供方。每个 child 行都会根据合并结果中携带持久化 `origin: 'subagent'` 的 header 派生读取时的 `hasChildren` 提示;它不会读取后代事件日志,展开后仍以描述符支撑的 child 目录为权威依据。UI 等服务消费方可以保留两种模式,并为无标签的一次性 child 选择回退展示;面向模型的 `list_agents` 工具只投影 `continuable` 条目,通过在线 Agent 注册表细化状态,并把仅存于存储的状态映射为可恢复而非终态的 `ready`(`running`/`idle`/`ready`),并在 `descendants` scope 下遍历 `listDescendants()`。列表操作会把调用方的取消信号转发到每次持久化读取,在这些 await 前后检查取消,并将每次检测到的中止报告为 `SubagentError` 错误码 `CANCELLED`;投影注册表未挂载则以 `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` 响亮失败,会话存储缺失则以 `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` 响亮失败。完整约定见[后台 subagent 任务 Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.md)、[可继续后台 subagent Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)、[持久化目录 Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md)、[服务合并 Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)、[能力 seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)和 `src/types.ts`。
|
|
108
|
+
面向模型的工具默认同步收集:先等待子 agent 结果,再 dispose 运行,然后才返回。一次性后台委派会在工具中注册普通 Task,其通用状态、收集和取消工具负责后续交互,并将模型提供的 `description` 持久化为可选显示标签。可继续后台委派会调用 `ctx.subagents.startContinuable()`,只返回持久化子 agent id;子 agent 自 inbox 接受起就拥有自己的轮次,因此没有 Task、也没有结果 promise——调用方通过 `send_message` 后续操作工具发送后续工作,`interrupt()` 只停止当前轮次而不 dispose 子 agent,而持久化子 agent 会话仍是子 agent 详细输出的来源。只有 `ctx.agents` 可用时,继续执行管理器才会存在,而会话持久化按每项继续执行操作解析。与此独立,`listChildren()` 枚举在线会话存储与可选会话持久化的在线优先合并——持久化缺席时仅枚举在线 child,因为那时冷 child 本就无法恢复——并由已注册的 `subagent` 投影单元供给每个 child 的持久化模式与标签:在线 child 取注册表的水位快照;冷 child 先取可选投影缓存的持久化行,且仅当其 `seq` 门证明该值折叠自 child 自身后缀(fork 种子之后——自有描述符一经追加即不可变)才直接采用,否则经一次有界并发的持久化 inspect 再经注册表折叠,且 inspect 结果必须仍指向枚举时的生命周期(同 id 被重新发布的会话降级为 `corrupt` diagnostic)。缓存读取抛出异常时,不会据此作出分类判断,因为缓存只是派生数据;静默落到该权威重折。分类结果完全以投影折叠为准;列表操作本身不解析描述符。取得身份值即产出 child 行;已定局而折叠未产出身份的候选是 `corrupt` diagnostic,inspect 失败是瞬时的 `unavailable`(下次列表重试),运行中而暂无身份值的候选整行省略(描述符尚未追加的创建窗口)。它不查询继续执行管理器、Agent 注册信息、Activation 或提供方。每个 child 行都会根据合并结果中携带持久化 `origin: 'subagent'` 的 header 派生读取时的 `hasChildren` 提示;它不会读取后代事件日志,展开后仍以描述符支撑的 child 目录为权威依据。UI 等服务消费方可以保留两种模式,并为无标签的一次性 child 选择回退展示;面向模型的 `list_agents` 工具只投影 `continuable` 条目,通过在线 Agent 注册表细化状态,并把仅存于存储的状态映射为可恢复而非终态的 `ready`(`running`/`idle`/`ready`),并在 `descendants` scope 下遍历 `listDescendants()`。列表操作会把调用方的取消信号转发到每次持久化读取,在这些 await 前后检查取消,并将每次检测到的中止报告为 `SubagentError` 错误码 `CANCELLED`;投影注册表未挂载则以 `SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE` 响亮失败,会话存储缺失则以 `SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE` 响亮失败。完整约定见[后台 subagent 任务 Agent Note](../../../.agents/notes/implemented/feature/2026-07-08-background-subagent-tasks.zh.md)、[可继续后台 subagent Agent Note](../../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.zh.md)、[持久化目录 Agent Note](../../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.zh.md)、[服务合并 Agent Note](../../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.zh.md)、[能力 seam Agent Note](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.zh.md)和 `src/types.ts`。
|
|
107
109
|
|
|
108
110
|
可继续 Activation 会等待 best-effort 的最终会话 flush,但不会把 listener 参与视为持久性确认。一次性运行保留尽力执行的会话检查点,因此已完成的一次性 child 只有在其会话确实进入持久化存储时,才可在 dispose 后继续被发现;如果该检查点缺失,服务不会根据 Task 历史虚构目录条目。
|
|
109
111
|
|
|
110
112
|
## 模型体验
|
|
111
113
|
|
|
114
|
+
<a id="settlement-notice"></a>
|
|
115
|
+
|
|
112
116
|
### 结算通知
|
|
113
117
|
|
|
114
118
|
#### 模型看到的内容
|
package/lib/index.js
CHANGED
|
@@ -1948,6 +1948,25 @@ function sameLifecycle(meta, expected) {
|
|
|
1948
1948
|
function assertListingNotCancelled(signal) {
|
|
1949
1949
|
if (signal?.aborted) throw new SubagentError("subagent listing was cancelled", "CANCELLED");
|
|
1950
1950
|
}
|
|
1951
|
+
//#endregion
|
|
1952
|
+
//#region lib/types/projection.js
|
|
1953
|
+
/**
|
|
1954
|
+
* Pure session projections for subagent identity (mode/label) and active-turn
|
|
1955
|
+
* duration.
|
|
1956
|
+
*
|
|
1957
|
+
* @module @deepseek-ai/dsh-subagent/projection
|
|
1958
|
+
*/
|
|
1959
|
+
const activeIntervalSchema = z.object({
|
|
1960
|
+
since: z.number().int().nonnegative(),
|
|
1961
|
+
through: z.number().int().nonnegative()
|
|
1962
|
+
}).strict();
|
|
1963
|
+
const projectionSchema = z.object({
|
|
1964
|
+
settledMs: z.number().int().nonnegative(),
|
|
1965
|
+
active: activeIntervalSchema.optional()
|
|
1966
|
+
}).strict().transform(({ settledMs, active }) => ({
|
|
1967
|
+
settledMs,
|
|
1968
|
+
...active === void 0 ? {} : { active }
|
|
1969
|
+
}));
|
|
1951
1970
|
/**
|
|
1952
1971
|
* Fold turn boundaries around the child's own durable descriptor.
|
|
1953
1972
|
*
|
|
@@ -1958,12 +1977,11 @@ function assertListingNotCancelled(signal) {
|
|
|
1958
1977
|
*/
|
|
1959
1978
|
const subagentTimingProjectionDefinition = {
|
|
1960
1979
|
key: "subagentTiming",
|
|
1961
|
-
|
|
1980
|
+
stateSchema: z.object({
|
|
1962
1981
|
settledMs: z.number().int().nonnegative(),
|
|
1963
|
-
active:
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
}).strict().optional()
|
|
1982
|
+
active: activeIntervalSchema.optional(),
|
|
1983
|
+
pendingTurnStart: z.number().int().nonnegative().optional(),
|
|
1984
|
+
descriptorSeen: z.boolean()
|
|
1967
1985
|
}).strict(),
|
|
1968
1986
|
init: () => ({
|
|
1969
1987
|
descriptorSeen: false,
|
|
@@ -2013,13 +2031,16 @@ const subagentTimingProjectionDefinition = {
|
|
|
2013
2031
|
}
|
|
2014
2032
|
};
|
|
2015
2033
|
},
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2034
|
+
wire: {
|
|
2035
|
+
viewSchema: projectionSchema,
|
|
2036
|
+
view: (state) => ({
|
|
2037
|
+
settledMs: state.settledMs,
|
|
2038
|
+
...state.active === void 0 ? {} : { active: state.active }
|
|
2039
|
+
})
|
|
2040
|
+
},
|
|
2020
2041
|
stateVersion: 2
|
|
2021
2042
|
};
|
|
2022
|
-
const
|
|
2043
|
+
const identityValueSchema = z.discriminatedUnion("mode", [z.object({
|
|
2023
2044
|
mode: z.literal("one-shot"),
|
|
2024
2045
|
label: z.string().optional(),
|
|
2025
2046
|
seq: z.number().int().nonnegative()
|
|
@@ -2027,7 +2048,9 @@ const identitySchema = z.discriminatedUnion("mode", [z.object({
|
|
|
2027
2048
|
mode: z.literal("continuable"),
|
|
2028
2049
|
label: z.string(),
|
|
2029
2050
|
seq: z.number().int().nonnegative()
|
|
2030
|
-
}).strict()])
|
|
2051
|
+
}).strict()]);
|
|
2052
|
+
const identitySchema = identityValueSchema.nullable();
|
|
2053
|
+
const identityStateSchema = z.object({ identity: identityValueSchema.optional() }).strict();
|
|
2031
2054
|
/** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
|
|
2032
2055
|
function descriptorIdentity(event) {
|
|
2033
2056
|
let descriptor;
|
|
@@ -2060,14 +2083,17 @@ function descriptorIdentity(event) {
|
|
|
2060
2083
|
*/
|
|
2061
2084
|
const subagentIdentityProjectionDefinition = {
|
|
2062
2085
|
key: "subagent",
|
|
2063
|
-
|
|
2086
|
+
stateSchema: identityStateSchema,
|
|
2064
2087
|
init: () => ({}),
|
|
2065
2088
|
apply: (state, event) => {
|
|
2066
2089
|
if (event.type !== "subagent/descriptor") return state;
|
|
2067
2090
|
const identity = descriptorIdentity(event);
|
|
2068
2091
|
return identity === void 0 ? {} : { identity };
|
|
2069
2092
|
},
|
|
2070
|
-
|
|
2093
|
+
wire: {
|
|
2094
|
+
viewSchema: identitySchema,
|
|
2095
|
+
view: (state) => state.identity ?? null
|
|
2096
|
+
},
|
|
2071
2097
|
stateVersion: 2
|
|
2072
2098
|
};
|
|
2073
2099
|
//#endregion
|
|
@@ -4,21 +4,29 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module @deepseek-ai/dsh-subagent/projection
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
8
|
-
import type {
|
|
9
|
-
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
9
|
+
import type { SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts';
|
|
10
|
+
/** Fold state for a subagent's latest timing snapshot. */
|
|
11
|
+
export interface TimingState {
|
|
10
12
|
/** Milliseconds accumulated across completed post-descriptor turns. */
|
|
11
13
|
settledMs: number;
|
|
12
14
|
/** Current open interval kept paired inside the fold. */
|
|
13
15
|
active?: {
|
|
14
16
|
since: number;
|
|
15
17
|
through: number;
|
|
16
|
-
};
|
|
18
|
+
} | undefined;
|
|
17
19
|
/** Latest pre-descriptor turn start, promoted when the child's own descriptor arrives. */
|
|
18
|
-
pendingTurnStart?: number;
|
|
20
|
+
pendingTurnStart?: number | undefined;
|
|
19
21
|
/** Whether the fold has crossed a descriptor in this logical log. */
|
|
20
22
|
descriptorSeen: boolean;
|
|
21
23
|
}
|
|
24
|
+
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
25
|
+
interface SessionProjectionStateMap {
|
|
26
|
+
subagentTiming: TimingState;
|
|
27
|
+
subagent: IdentityState;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
22
30
|
/**
|
|
23
31
|
* Fold turn boundaries around the child's own durable descriptor.
|
|
24
32
|
*
|
|
@@ -27,10 +35,39 @@ interface TimingState {
|
|
|
27
35
|
* admits only a child with exactly one descriptor in its own suffix, making
|
|
28
36
|
* the final reset the child's authoritative timing origin.
|
|
29
37
|
*/
|
|
30
|
-
export declare const subagentTimingProjectionDefinition:
|
|
38
|
+
export declare const subagentTimingProjectionDefinition: {
|
|
39
|
+
key: "subagentTiming";
|
|
40
|
+
stateSchema: z.ZodType<TimingState, unknown, z.core.$ZodTypeInternals<TimingState, unknown>>;
|
|
41
|
+
init: () => {
|
|
42
|
+
descriptorSeen: false;
|
|
43
|
+
settledMs: number;
|
|
44
|
+
};
|
|
45
|
+
apply: (state: NoInfer<TimingState>, event: SessionEvent) => {
|
|
46
|
+
/** Milliseconds accumulated across completed post-descriptor turns. */
|
|
47
|
+
settledMs: number;
|
|
48
|
+
/** Current open interval kept paired inside the fold. */
|
|
49
|
+
active?: {
|
|
50
|
+
since: number;
|
|
51
|
+
through: number;
|
|
52
|
+
} | undefined;
|
|
53
|
+
/** Whether the fold has crossed a descriptor in this logical log. */
|
|
54
|
+
descriptorSeen: boolean;
|
|
55
|
+
};
|
|
56
|
+
wire: {
|
|
57
|
+
viewSchema: z.ZodType<SubagentTimingProjection, unknown, z.core.$ZodTypeInternals<SubagentTimingProjection, unknown>>;
|
|
58
|
+
view: (state: NoInfer<TimingState>) => {
|
|
59
|
+
active?: {
|
|
60
|
+
since: number;
|
|
61
|
+
through: number;
|
|
62
|
+
};
|
|
63
|
+
settledMs: number;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
stateVersion: number;
|
|
67
|
+
};
|
|
31
68
|
interface IdentityState {
|
|
32
69
|
/** Identity from the last valid descriptor; absent before one, and after an invalid one. */
|
|
33
|
-
identity?: SubagentIdentityProjection;
|
|
70
|
+
identity?: SubagentIdentityProjection | undefined;
|
|
34
71
|
}
|
|
35
72
|
/**
|
|
36
73
|
* Fold the durable mode/label identity from `subagent/descriptor` events,
|
|
@@ -43,6 +80,16 @@ interface IdentityState {
|
|
|
43
80
|
* holding the earlier identity replaces it instead of keeping it stale;
|
|
44
81
|
* `null` ⟺ no valid descriptor, with the causes deliberately undistinguished.
|
|
45
82
|
*/
|
|
46
|
-
export declare const subagentIdentityProjectionDefinition:
|
|
83
|
+
export declare const subagentIdentityProjectionDefinition: {
|
|
84
|
+
key: "subagent";
|
|
85
|
+
stateSchema: z.ZodType<IdentityState, unknown, z.core.$ZodTypeInternals<IdentityState, unknown>>;
|
|
86
|
+
init: () => {};
|
|
87
|
+
apply: (state: NoInfer<IdentityState>, event: SessionEvent) => IdentityState;
|
|
88
|
+
wire: {
|
|
89
|
+
viewSchema: z.ZodNullable<z.ZodType<SubagentIdentityProjection, unknown, z.core.$ZodTypeInternals<SubagentIdentityProjection, unknown>>>;
|
|
90
|
+
view: (state: NoInfer<IdentityState>) => SubagentIdentityProjection | null;
|
|
91
|
+
};
|
|
92
|
+
stateVersion: number;
|
|
93
|
+
};
|
|
47
94
|
export {};
|
|
48
95
|
//# sourceMappingURL=projection.d.ts.map
|
package/lib/types/projection.js
CHANGED
|
@@ -6,14 +6,22 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { foldSubagentDescriptor } from "./descriptor.js";
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const activeIntervalSchema = z.object({
|
|
10
|
+
since: z.number().int().nonnegative(),
|
|
11
|
+
through: z.number().int().nonnegative(),
|
|
12
|
+
}).strict();
|
|
11
13
|
const projectionSchema = z.object({
|
|
12
14
|
settledMs: z.number().int().nonnegative(),
|
|
13
|
-
active:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
15
|
+
active: activeIntervalSchema.optional(),
|
|
16
|
+
}).strict().transform(({ settledMs, active }) => ({
|
|
17
|
+
settledMs,
|
|
18
|
+
...active === undefined ? {} : { active },
|
|
19
|
+
}));
|
|
20
|
+
const timingStateSchema = z.object({
|
|
21
|
+
settledMs: z.number().int().nonnegative(),
|
|
22
|
+
active: activeIntervalSchema.optional(),
|
|
23
|
+
pendingTurnStart: z.number().int().nonnegative().optional(),
|
|
24
|
+
descriptorSeen: z.boolean(),
|
|
17
25
|
}).strict();
|
|
18
26
|
/**
|
|
19
27
|
* Fold turn boundaries around the child's own durable descriptor.
|
|
@@ -25,7 +33,7 @@ const projectionSchema = z.object({
|
|
|
25
33
|
*/
|
|
26
34
|
export const subagentTimingProjectionDefinition = {
|
|
27
35
|
key: 'subagentTiming',
|
|
28
|
-
|
|
36
|
+
stateSchema: timingStateSchema,
|
|
29
37
|
init: () => ({ descriptorSeen: false, settledMs: 0 }),
|
|
30
38
|
apply: (state, event) => {
|
|
31
39
|
if (event.type === 'turn/start') {
|
|
@@ -62,10 +70,13 @@ export const subagentTimingProjectionDefinition = {
|
|
|
62
70
|
return state;
|
|
63
71
|
return { ...state, active: { ...state.active, through: event.time } };
|
|
64
72
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
wire: {
|
|
74
|
+
viewSchema: projectionSchema,
|
|
75
|
+
view: state => ({
|
|
76
|
+
settledMs: state.settledMs,
|
|
77
|
+
...(state.active === undefined ? {} : { active: state.active }),
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
69
80
|
stateVersion: 2,
|
|
70
81
|
};
|
|
71
82
|
// The cast bridges only the optional-label arm: Zod's optional output
|
|
@@ -73,7 +84,7 @@ export const subagentTimingProjectionDefinition = {
|
|
|
73
84
|
// from the public interface. The no-value state itself is the serializable
|
|
74
85
|
// `null` arm — never `undefined` — so every registry read and push frame
|
|
75
86
|
// survives JSON.stringify losslessly.
|
|
76
|
-
const
|
|
87
|
+
const identityValueSchema = z.discriminatedUnion('mode', [
|
|
77
88
|
z.object({
|
|
78
89
|
mode: z.literal('one-shot'),
|
|
79
90
|
label: z.string().optional(),
|
|
@@ -84,7 +95,11 @@ const identitySchema = z.discriminatedUnion('mode', [
|
|
|
84
95
|
label: z.string(),
|
|
85
96
|
seq: z.number().int().nonnegative(),
|
|
86
97
|
}).strict(),
|
|
87
|
-
])
|
|
98
|
+
]);
|
|
99
|
+
const identitySchema = identityValueSchema.nullable();
|
|
100
|
+
const identityStateSchema = z.object({
|
|
101
|
+
identity: identityValueSchema.optional(),
|
|
102
|
+
}).strict();
|
|
88
103
|
/** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
|
|
89
104
|
function descriptorIdentity(event) {
|
|
90
105
|
let descriptor;
|
|
@@ -119,7 +134,7 @@ function descriptorIdentity(event) {
|
|
|
119
134
|
*/
|
|
120
135
|
export const subagentIdentityProjectionDefinition = {
|
|
121
136
|
key: 'subagent',
|
|
122
|
-
|
|
137
|
+
stateSchema: identityStateSchema,
|
|
123
138
|
init: () => ({}),
|
|
124
139
|
apply: (state, event) => {
|
|
125
140
|
if (event.type !== 'subagent/descriptor')
|
|
@@ -127,7 +142,7 @@ export const subagentIdentityProjectionDefinition = {
|
|
|
127
142
|
const identity = descriptorIdentity(event);
|
|
128
143
|
return identity === undefined ? {} : { identity };
|
|
129
144
|
},
|
|
130
|
-
view: state => state.identity ?? null,
|
|
145
|
+
wire: { viewSchema: identitySchema, view: state => state.identity ?? null },
|
|
131
146
|
// Bumped when the identity gained its `seq` field: an older checkpoint row
|
|
132
147
|
// would replay into a value the schema rejects, so it must refold instead.
|
|
133
148
|
stateVersion: 2,
|
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.
|
|
4
|
+
"version": "0.1.1-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -40,22 +40,22 @@
|
|
|
40
40
|
"zod": "^4.4.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
44
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.
|
|
45
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
46
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
47
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.
|
|
50
|
-
"@deepseek-ai/dsh-
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-session-
|
|
53
|
-
"@deepseek-ai/dsh-session-
|
|
54
|
-
"@deepseek-ai/dsh-session-projection-cache": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-jobs": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/
|
|
58
|
-
"@deepseek-ai/
|
|
43
|
+
"@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.1-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-brand": "^0.1.1-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.1-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-scope": "^0.1.1-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-projection-cache": "^0.1.1-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-jobs": "^0.1.1-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.1-rc.2",
|
|
57
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
58
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependenciesMeta": {
|
|
61
61
|
"@deepseek-ai/dsh-agent-presets": {
|
|
@@ -84,23 +84,23 @@
|
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
88
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.
|
|
89
|
-
"@deepseek-ai/dsh-brand": "^0.1.
|
|
90
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
91
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
92
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
93
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.
|
|
94
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
95
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
96
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
97
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
98
|
-
"@deepseek-ai/dsh-session-projection-cache": "^0.1.
|
|
99
|
-
"@deepseek-ai/dsh-storage": "^0.1.
|
|
100
|
-
"@deepseek-ai/dsh-storage-domain": "^0.1.
|
|
101
|
-
"@deepseek-ai/dsh-
|
|
102
|
-
"@deepseek-ai/
|
|
103
|
-
"@deepseek-ai/dsh-
|
|
104
|
-
"@deepseek-ai/
|
|
87
|
+
"@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.1-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-brand": "^0.1.1-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.1-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-scope": "^0.1.1-rc.2",
|
|
95
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
96
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
|
|
97
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
98
|
+
"@deepseek-ai/dsh-session-projection-cache": "^0.1.1-rc.2",
|
|
99
|
+
"@deepseek-ai/dsh-storage": "^0.1.1-rc.2",
|
|
100
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.1-rc.2",
|
|
101
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.1-rc.2",
|
|
102
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
103
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
|
|
104
|
+
"@deepseek-ai/dsh-jobs": "^0.1.1-rc.2"
|
|
105
105
|
}
|
|
106
106
|
}
|