feat(agent-presets): enable background Codex and Claude Code subagent tasks

This commit is contained in:
pku-xht
2026-08-12 17:07:34 +08:00
parent b423ed15d7
commit 28fcda2751
35 changed files with 318 additions and 102 deletions
@@ -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/architecture/2026-08-10-fork-children-stay-one-shot.md
2026-08-10-fork-children-stay-one-shot.md: 030bed3c1b516a54afd5f97b00000ed7ceaf64d8
2026-08-10-fork-children-stay-one-shot.zh.md: 4b033a70c3674315c491e85be67a1d357aeac51b
2026-08-10-fork-children-stay-one-shot.md: f799f9f7affe7dfecbbe12d0988cf6f9b936d43e
2026-08-10-fork-children-stay-one-shot.zh.md: 5c55e96296ed6678ac82afc0e40150d443f74c96
@@ -12,7 +12,7 @@ The child-scoped `report` return channel is now the largest such addition, and s
## Decision
Every shipped composition binds the fork delegation tool to `backgroundMode: one-shot`: [the base bundle](../../../../packages/bundle/base/cordis.patch.yml), [the ACP example](../../../../examples/acp-agent/cordis.yml), and [the headless example](../../../../examples/headless-agent/cordis.yml). The base bundle leaves `run_in_background` available, because it mounts a task service; the two examples set `enableRunInBackground: false`, because they mount none and a one-shot background start would otherwise fail at call time on a missing `tasks` service.
Every shipped composition binds the fork delegation tool to `backgroundMode: one-shot`: [the base bundle](../../../../packages/bundle/base/cordis.patch.yml), [the ACP example](../../../../examples/acp-agent/cordis.yml), and [the headless example](../../../../examples/headless-agent/cordis.yml). All three have access to the generic Task runtime: the base bundle leaves `run_in_background` available, while the two examples set `enableRunInBackground: false` as an explicit foreground-only composition choice.
One-shot children — foreground and background alike — are created through `SubagentService.start()`, which never enters the continuable activation-setup registry, so neither `report` nor its prompt section is installed. A forked one-shot child's system prompt and tool schemas therefore equal its parent's, apart from the `persona` and `toolFilter` deltas a deployment opts into per delegation tool.
@@ -38,7 +38,7 @@ The reintroduction condition is recorded as a `TODO(fork-continuable-prefix-reus
## Consequences
- No shipped composition creates a continuable forked child; `subagent_fork` returns a result to its caller's turn, and `send_message` addresses only spawned children.
- No shipped composition creates a continuable forked child. The base bundle may return a one-shot Task id for `subagent_fork`, while the ACP and headless examples return the result to the caller's turn; `send_message` addresses only spawned children.
- A forked child's request prefix stays byte-identical to its parent's unless the deployment configures `persona` or `toolFilter` on the fork delegation tool, so the token cost of seeding buys provider-side reuse again.
- The fork provider's continuable path has no production caller and no assembled-composition coverage. It keeps its package-level tests, and the seam still accepts it, so a bundle or `--patch` overlay can reintroduce it with no code change and no warning.
- `subagent_fork`'s model-visible schema changes: the continuable background wording is replaced by the one-shot task wording in the base bundle, and disappears entirely from the two examples. The affected keyless snapshot tool-schema sidecars are re-recorded in the same change.
@@ -12,7 +12,7 @@ fork 与 spawn 的唯一区别是 child 的 Session 会以 parent 已完成轮
## 决策
所有随附组合都把 fork 委派工具绑定为 `backgroundMode: one-shot`[base 组合包](../../../../packages/bundle/base/cordis.patch.yml)、[ACP 示例](../../../../examples/acp-agent/cordis.yml)与[headless 示例](../../../../examples/headless-agent/cordis.yml)。base 组合包保留 `run_in_background`因为它挂载了 task 服务;两个示例设置 `enableRunInBackground: false`,因为它们都不挂载 task 服务,否则一次 one-shot 后台启动会在调用时因缺少 `tasks` 服务而失败
所有随附组合都把 fork 委派工具绑定为 `backgroundMode: one-shot`[base 组合包](../../../../packages/bundle/base/cordis.patch.yml)、[ACP 示例](../../../../examples/acp-agent/cordis.yml)与[headless 示例](../../../../examples/headless-agent/cordis.yml)。三者都能使用通用 Task 运行时:base 组合包保留 `run_in_background`,两个示例则把 `enableRunInBackground: false` 作为显式的仅前台组装选择
one-shot child——前台与后台皆然——经由 `SubagentService.start()` 创建,该路径从不进入可继续的 activation setup 注册表,因此 `report` 与它的提示词 section 都不会被安装。于是一个 fork 出的 one-shot child 的系统提示词与工具 schema 与其 parent 相同,只差部署逐个委派工具主动选择的 `persona``toolFilter` 增量。
@@ -38,7 +38,7 @@ one-shot child——前台与后台皆然——经由 `SubagentService.start()`
## 后果
- 没有任何随附组合会创建可继续的 fork child`subagent_fork` 把结果返回给调用方的轮次,而 `send_message` 只寻址 spawn 出的 child。
- 没有任何随附组合会创建可继续的 fork child。base 组合包中的 `subagent_fork` 可以返回 one-shot Task id,而 ACP 与 headless 示例会把结果返回给调用方的轮次`send_message` 只寻址 spawn 出的 child。
- 除非部署在 fork 委派工具上配置了 `persona``toolFilter`fork child 的请求前缀与其 parent 逐字节相同,因此初始内容的 token 成本重新换来了提供方侧的复用。
- fork 提供方的可继续路径没有生产调用方,也没有整体组装层面的覆盖。它保留自己的包内测试,seam 也仍然接受它,因此某个组合包或 `--patch` 覆盖层可以无需改动代码、也不会有任何警告地把它重新引入。
- `subagent_fork` 面向模型的 schema 发生变化:base 组合包中可继续的后台措辞被 one-shot 的 task 措辞取代,在两个示例中则完全消失。受影响的无密钥快照工具 schema 伴随文件在同一次改动中重新记录。
@@ -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-08-04-claude-code-and-codex-subagent-backends.md
2026-08-04-claude-code-and-codex-subagent-backends.md: ccc96d6c998c4ab958a7eea1e502d036d16ec90d
2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 740eeb633e336d5b01cb0b84fb656612e690959d
2026-08-04-claude-code-and-codex-subagent-backends.md: 99801d6bbd9c67e6c9215d4e10c77fb70d5e7525
2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 0944f1356ff334cc284def91d5ce0924ce950b83
@@ -12,24 +12,25 @@ The product integrations must not become second owners for task text, cwd, cance
## Decision
The harness publishes two sibling one-shot provider packages: `codex` and `claude-code`. This note owns their product protocols, result mapping, and process lifecycle; the [shared-profile-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md) supersedes the original opt-in composition placement. Loading either provider starts no product process, and each tool accepts only a standalone text task; product selection and background execution are not model arguments.
The harness publishes two sibling one-shot provider packages: `codex` and `claude-code`. This note owns their product protocols, result mapping, and process lifecycle; the [shared-profile-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md) owns provider placement, and the [product one-shot background decision](2026-08-12-product-subagent-one-shot-background-tasks.md) owns the model-visible scheduling choice. Loading either provider starts no product process, and each tool accepts only a standalone text task; product selection remains deployment configuration.
Both providers report `inheritsParentContext: false`, advertise no optional start capabilities, and pass the parent Session cwd without copying the parent conversation. Their documented tools disable background execution and use `maxDepth: 'provider-managed'`, leaving recursion policy with the out-of-process product instead of sending a limit the provider cannot enforce. Every call creates a fresh product process and a non-resumable product conversation. The shared subagent service continues to own request resolution, lifecycle events, result settlement, and foreground collection; the shared subprocess service owns credential scrubbing, process-tree termination, and whole-tree exit observation.
Both providers report `inheritsParentContext: false`, advertise no optional start capabilities, and pass the parent Session cwd without copying the parent conversation. Their documented tools use `backgroundMode: 'one-shot'` and `maxDepth: 'provider-managed'`: the consumer keeps foreground collection as the default and may place the same run in the generic Task runtime, while recursion policy stays with the out-of-process product. Every call creates a fresh product process and a non-resumable product conversation. `ctx.subagents` owns named-request resolution and paired lifecycle events; `dsh-tool-subagent` owns model-visible scheduling and foreground-versus-Task adaptation; `ctx.tasks` and `dsh-tool-tasks` own Task ids, state, output, controls, notices, and parent-owner cancellation; each product provider owns native result mapping, while `dsh-subprocess` owns credential scrubbing, process-tree termination, and whole-tree exit observation.
```text
fixed tool → shared subagent service → product provider → official product process
← final answer / explicit error / cancellation ← terminal product fact
→ foreground disposal → shared process-tree termination → whole-tree exit
fixed tool -> dsh-tool-subagent -> ctx.subagents -> product provider -> product process
foreground <- final product outcome
background -> ctx.tasks / dsh-tool-tasks -> Task id / state / notice / controls
both -> provider disposal -> dsh-subprocess -> whole-tree exit
```
### Ownership and lifecycle
| Phase | Shared owner | Product-specific responsibility | Observable result |
| Layer | Owner | Responsibility | Observable result |
| --- | --- | --- | --- |
| Resolve | `dsh-tool-subagent` and `ctx.subagents` | Validate the product's text-only input and derive native startup parameters | Unsupported context or malformed input fails before a run is published |
| Start | `dsh-subprocess` owns every acquired process tree | Reach the smallest native point at which the product conversation and process can both be controlled | `start()` publishes one existing `SubagentRun`, or cleans up and rejects |
| Run | The product owns its native protocol facts; the holder owns their mapping | Submit exactly one task and derive an existing shared stop reason; Codex uses `max-tokens` only for explicit context exhaustion | The parent receives only a final answer or an explicit failure |
| Dispose | The foreground consumer requests release; `dsh-subprocess` proves exit | Close the native protocol and express any best-effort native cancellation | Disposal is idempotent and returns only after the whole process tree exits |
| Delegation lifecycle | `ctx.subagents` | Resolve the named provider request and pair lifecycle events around the published `SubagentRun` | Unsupported context or malformed input fails before a run is published; start and terminal events remain paired |
| Scheduling and adaptation | `dsh-tool-subagent` | Interpret `run_in_background`, choose foreground collection or one-shot Task registration, and map the shared stop reason | Foreground returns the product outcome; background returns a Task id after registration |
| Task state and control | `ctx.tasks` and `dsh-tool-tasks` | Own Task state, output, cancellation, owner cleanup, completion notices, and model-facing controls | The exact parent can collect, list, or stop background work and receives its completion notice |
| Native run and teardown | Product provider and `dsh-subprocess` | Produce one native result, close the product protocol, request best-effort native cancellation, and prove process-tree exit | Foreground return and Task settlement both wait for idempotent disposal and whole-tree exit |
## Codex provider
@@ -59,7 +60,7 @@ The credentialed Claude Code e2e uses the official DeepSeek Claude Code contract
## Distribution and evidence
Each product owns branch-complete package tests, a required keyless real-product spec, a Loader composition e2e, and a credentialed DeepSeek e2e. The keyless product tier uses the exact official distribution under test, a non-empty fake product key, an isolated temporary workspace and product home, and a loopback fixed-answer model. Missing product requests, wrong authentication, altered task text, a non-exact answer, a skipped real product, or a surviving managed handle fails the required test. The Loader tier boots the README-shaped user configuration, verifies both fixed foreground-only tools in one context, and starts neither product process. The credentialed tier starts the same production provider and real product with a runtime-only key, requires a unique nonce from the fixed official DeepSeek service, and proves quiescence again; it self-skips only when a local operator supplied no key, while trusted CI preflights the secret.
Each product owns branch-complete package tests, a required keyless real-product spec, a Loader composition e2e, and a credentialed DeepSeek e2e. The keyless product tier uses the exact official distribution under test, a non-empty fake product key, an isolated temporary workspace and product home, and a loopback fixed-answer model. Missing product requests, wrong authentication, altered task text, a non-exact answer, a skipped real product, or a surviving managed handle fails the required test. The Loader tier boots the README-shaped user configuration, verifies both fixed one-shot tools expose optional background scheduling alongside generic task controls, and starts neither product process. The credentialed tier starts the same production provider and real product with a runtime-only key, requires a unique nonce from the fixed official DeepSeek service, and proves quiescence again; it self-skips only when a local operator supplied no key, while trusted CI preflights the secret.
The Codex evidence pins `@openai/codex@0.147.0` and `codex-cli 0.147.0`. Its real-product spec observes the exact Bearer key, original task, byte-exact final answer, unattended command rejection with no file side effect, local cancellation, and whole-tree exit. Production still supplies `codex` on `PATH`.
@@ -83,12 +84,12 @@ The project owner's distribution authorization is scoped to the official `@anthr
**Plugin-managed login, product home, models, settings, or permissions.** Those choices would create another authority beside each product's native configuration and enlarge a one-shot provider into account management. The providers expose only an explicit environment overlay and teardown grace; unattended interaction fails closed.
**Continuation, progress, background collection, and shared parent context.** The delivered user result is one self-contained task and one final answer. Product sessions, resume, follow-up, intermediate messages, parent transcript transfer, structured output, and background collection need separate user contracts and are not prebuilt.
**Continuation, progress, product-native background state, and shared parent context.** The provider payload remains one final answer for one self-contained task. The generic Task layer may add its id, status, notice, collection, and cancellation results, but product sessions, resume, follow-up, intermediate messages, parent transcript transfer, structured output, and provider-specific background state need separate user contracts and are not prebuilt.
## Consequences
Users delegate through two stable foreground tools backed by the official product integrations. Their Profile placement and per-Preset exposure are owned by the [shared-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md); this note's provider lifecycle keeps native settings and behavior while shared services retain the sole ownership of task settlement and process-tree quiescence.
Users delegate through two stable one-shot tools backed by the official product integrations. Their Profile placement and per-Preset exposure are owned by the [shared-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md), and their foreground-default optional Task scheduling is owned by the [product one-shot background decision](2026-08-12-product-subagent-one-shot-background-tasks.md). This note's provider lifecycle keeps native settings and behavior while shared services retain the sole ownership of task settlement and process-tree quiescence.
Every delegation pays for a fresh product process and independent model context, and only final text reaches the parent. Product-native configuration makes behavior depend on the deployment's installed product, account state, and workspace settings. Credentialed e2e runs also spend external API quota and depend on the official DeepSeek endpoint; deterministic protocol, failure, cancellation, and approval coverage remains in the keyless tier. The providers do not resume sessions, stream progress, accept new human interaction, roll back tool or file side effects, or impose a wall-clock timeout.
Every delegation pays for a fresh product process and independent model context. The product payload reaching the parent is final text only; background scheduling additionally exposes generic Task ids, status, completion notices, and collection or cancellation results. Product-native configuration makes behavior depend on the deployment's installed product, account state, and workspace settings. Credentialed e2e runs also spend external API quota and depend on the official DeepSeek endpoint; deterministic protocol, failure, cancellation, and approval coverage remains in the keyless tier. The providers do not resume sessions, stream progress, accept new human interaction, roll back tool or file side effects, or impose a wall-clock timeout.
Compatibility is pinned by package-level unit coverage, keyless real-product loopback tests, credentialed DeepSeek nonce tests, public Loader composition, built-package and NodeNext consumer checks, generated documentation and notices, and the repository CI matrix. A supported product or DeepSeek endpoint/model baseline change must refresh those facts; production performs no separate runtime version probe.
@@ -12,24 +12,25 @@ Status: implemented
## 决策
harness 交付两个同级的一次性提供方包:`codex``claude-code`。本说明负责它们的产品协议、结果映射和进程生命周期;[共享 profile 宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)取代原先由用户选择启用的组装位置。加载任一提供方都不会启动产品进程,而且每个工具只接受独立文本任务;产品选择与后台执行都不作为模型参数
harness 交付两个同级的一次性提供方包:`codex``claude-code`。本说明负责它们的产品协议、结果映射和进程生命周期;[共享 profile 宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责提供方位置,[产品一次性后台任务决策](2026-08-12-product-subagent-one-shot-background-tasks.md)负责模型可见的调度选择。加载任一提供方都不会启动产品进程,而且每个工具只接受独立文本任务;产品选择仍属于部署配置
这两个提供方都报告 `inheritsParentContext: false`,不声明任何可选的启动能力,并传递父会话 cwd,但不会复制父级对话。文档所示的工具会禁用后台执行,并使用 `maxDepth: 'provider-managed'`,将递归策略留给进程外产品,而不是发送提供方无法强制执行的限制。每次调用都会创建一个全新的产品进程和一次不可续接的产品对话。共享 subagent 服务继续负责请求解析生命周期事件、结果结算和前台收集;共享子进程服务负责凭证清洗、进程树终止以及整棵进程树的退出观测。
这两个提供方都报告 `inheritsParentContext: false`,不声明任何可选的启动能力,并传递父会话 cwd,但不会复制父级对话。文档所示的工具使用 `backgroundMode: 'one-shot'``maxDepth: 'provider-managed'`:消费方默认在前台收集结果,也可把同一次运行放入通用 Task 运行时,而递归策略仍由进程外产品负责。每次调用都会创建一个全新的产品进程和一次不可续接的产品对话。`ctx.subagents` 负责具名请求解析与成对生命周期事件`dsh-tool-subagent` 负责模型可见的调度以及前台与 Task 适配;`ctx.tasks``dsh-tool-tasks` 负责 Task id、状态、输出、控制、通知与父级 owner 取消;各产品提供方负责原生结果映射,`dsh-subprocess`负责凭证清洗、进程树终止以及整棵进程树的退出观测。
```text
fixed tool → shared subagent service → product provider → official product process
← final answer / explicit error / cancellation ← terminal product fact
→ foreground disposal → shared process-tree termination → whole-tree exit
fixed tool -> dsh-tool-subagent -> ctx.subagents -> product provider -> product process
foreground <- final product outcome
background -> ctx.tasks / dsh-tool-tasks -> Task id / state / notice / controls
both -> provider disposal -> dsh-subprocess -> whole-tree exit
```
### 归属与生命周期
| 阶段 | 共享责任方 | 产品特定职责 | 可观察结果 |
| 层级 | 责任方 | 职责 | 可观察结果 |
| --- | --- | --- | --- |
| 解析 | `dsh-tool-subagent` `ctx.subagents` | 验证产品的纯文本输入并推导原生启动参数 | 不受支持的上下文或格式错误的输入会在发布运行前报错 |
| 启动 | `dsh-subprocess` 负责每棵已获取的进程树 | 到达能够同时控制产品对话与进程的最小原生控制点 | `start()` 发布一个已存在的 `SubagentRun`否则清理后拒绝调用 |
| 运行 | 产品负责其原生协议事实;持有方负责映射这些事实 | 只提交一项任务,并推导出一种现有的共享停止原因;Codex 仅在明确发生上下文耗尽时使用 `max-tokens` | 父级只会收到最终回答或明确失败 |
| dispose(资源释放) | 前台消费方请求释放;`dsh-subprocess` 证明进程已退出 | 关闭原生协议,并发出尽力而为的原生取消请求 | 释放操作具有幂等性,且仅在整棵进程树退出后才返回 |
| 委派生命周期 | `ctx.subagents` | 解析具名提供方请求,并为已发布的 `SubagentRun` 配对生命周期事件 | 不受支持的上下文或格式错误的输入会在发布运行前报错;启动与终态事件保持成对 |
| 调度与适配 | `dsh-tool-subagent` | 解释 `run_in_background`选择前台收集或 one-shot Task 登记,并映射共享停止原因 | 前台返回产品结果;后台在登记完成后返回 Task id |
| Task 状态与控制 | `ctx.tasks``dsh-tool-tasks` | 负责 Task 状态、输出、取消、owner 清理、完成通知与面向模型的控制工具 | 准确父级可以收集、列出或停止后台工作,并收到完成通知 |
| 原生运行与清理 | 产品提供方与 `dsh-subprocess` | 产生一个原生结果、关闭产品协议、请求尽力而为的原生取消,并证明进程树退出 | 前台返回与 Task 结算都会等待幂等资源释放和整棵进程树退出 |
## Codex 提供方
@@ -59,7 +60,7 @@ Codex 0.147.0 使用 Responses 协议,而 DeepSeek 的公开 OpenAI 兼容端
## 分发与证据
每个产品都负责覆盖所有分支的包测试、一项必跑的无密钥真实产品测试、一项 Loader 组合 e2e 和一项带密钥 DeepSeek e2e。无密钥产品层级使用被测的确切官方发行版、非空的伪产品密钥、隔离的临时工作区与产品主目录,以及能返回固定答案的回环模型。产品请求缺失、身份验证错误、任务文本被改动、答案不完全一致、真实产品被跳过或受管句柄仍存活,都会使这项必跑测试失败。Loader 层级会启动 README 所示形态的用户配置,在同一个上下文中验证两个固定且只支持前台执行的工具,并且不会启动任何产品进程。带密钥层级会使用仅在运行时提供的密钥启动同一生产提供方与真实产品,要求从固定的 DeepSeek 官方服务取得唯一随机数,并再次证明完全停稳;仅当本地操作者未提供密钥时才会自行跳过,而受信任的 CI 会预检该 secret。
每个产品都负责覆盖所有分支的包测试、一项必跑的无密钥真实产品测试、一项 Loader 组合 e2e 和一项带密钥 DeepSeek e2e。无密钥产品层级使用被测的确切官方发行版、非空的伪产品密钥、隔离的临时工作区与产品主目录,以及能返回固定答案的回环模型。产品请求缺失、身份验证错误、任务文本被改动、答案不完全一致、真实产品被跳过或受管句柄仍存活,都会使这项必跑测试失败。Loader 层级会启动 README 所示形态的用户配置,在同一个上下文中验证两个固定一次性工具会与通用任务控制工具一起公开可选后台调度,而且不会启动任何产品进程。带密钥层级会使用仅在运行时提供的密钥启动同一生产提供方与真实产品,要求从固定的 DeepSeek 官方服务取得唯一随机数,并再次证明完全停稳;仅当本地操作者未提供密钥时才会自行跳过,而受信任的 CI 会预检该 secret。
Codex 证据锁定 `@openai/codex@0.147.0``codex-cli 0.147.0`。其真实产品测试会观测确切的 Bearer 密钥、原始任务、逐字节完全一致的最终回答、不会产生文件副作用的无人值守命令拒绝、本地取消以及整棵进程树退出。生产环境仍提供 `codex`,并通过 `PATH` 解析。
@@ -83,12 +84,12 @@ Claude Code 证据锁定 Agent SDK 0.3.220,并使用 SDK 按平台分发的 Cl
**由插件管理登录、产品主目录、模型、设置或权限。** 这些选择会在每个产品的原生配置之外建立另一套权威来源,并将一次性提供方扩张为账户管理功能。提供方只公开显式环境覆盖项和清理宽限期;无人值守交互会以默认拒绝方式失败。
**续接、进度、后台收集和共享父级上下文。** 已交付的用户结果是一项自包含任务一个最终回答。产品会话、恢复、后续交互、中间消息、父级 transcript(文本记录)传递、结构化输出和后台收集都需要独立的用户约定,当前实现不会预先构建这些功能。
**续接、进度、产品原生后台状态和共享父级上下文。** 提供方载荷仍是一项自包含任务一个最终回答。通用 Task 层可以额外提供 id、状态、通知、收集与取消结果,但产品会话、恢复、后续交互、中间消息、父级 transcript(文本记录)传递、结构化输出和提供方专属后台状态都需要独立的用户约定,当前实现不会预先构建这些功能。
## 后果
用户通过官方产品集成支持的两个稳定前台工具进行委派。它们在 Profile 中的归属和按 Preset 暴露方式由[共享宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责本说明规定的提供方生命周期会保留原生设置与行为,而共享服务继续独占任务结算与进程树完全停稳的责任。
用户通过官方产品集成支持的两个稳定一次性工具进行委派。它们在 Profile 中的归属和按 Preset 暴露方式由[共享宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责,默认前台且可选通用 Task 的调度方式由[产品一次性后台任务决策](2026-08-12-product-subagent-one-shot-background-tasks.md)负责。本说明规定的提供方生命周期会保留原生设置与行为,而共享服务继续独占任务结算与进程树完全停稳的责任。
每次委派都要承担新建产品进程和独立模型上下文的开销,且只有最终文本会到达父级。产品原生配置使行为取决于部署环境中安装的产品、账户状态和工作区设置。带密钥 e2e 运行还会消耗外部 API 配额,并依赖 DeepSeek 官方端点;对协议、失败、取消与审批的确定性覆盖仍由无密钥层级承担。提供方不会恢复会话、以流式方式传送进度、接受新的人工交互、回滚工具或文件副作用,也不会施加按实际经过时间触发的超时。
每次委派都要承担新建产品进程和独立模型上下文的开销。到达父级的产品载荷仍只有最终文本;后台调度还会额外公开通用 Task id、状态、完成通知以及收集或取消结果。产品原生配置使行为取决于部署环境中安装的产品、账户状态和工作区设置。带密钥 e2e 运行还会消耗外部 API 配额,并依赖 DeepSeek 官方端点;对协议、失败、取消与审批的确定性覆盖仍由无密钥层级承担。提供方不会恢复会话、以流式方式传送进度、接受新的人工交互、回滚工具或文件副作用,也不会施加按实际经过时间触发的超时。
兼容性由包级单元测试覆盖率、无密钥真实产品回环测试、带密钥 DeepSeek 随机数测试、公开 Loader 组合、已构建包与 NodeNext 消费方检查、生成的文档与声明以及仓库 CI 矩阵共同锁定。更改受支持的产品基线或 DeepSeek 端点/模型基线时必须刷新这些事实;生产环境不会另行执行运行时版本探测。
@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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-08-12-product-subagent-one-shot-background-tasks.md
2026-08-12-product-subagent-one-shot-background-tasks.md: bcc4d9b947e5e666161fd2c2b447fa2edf86bd69
2026-08-12-product-subagent-one-shot-background-tasks.zh.md: 3aa8266788c48cd5301dde65dab2cb9d58b4145b
@@ -0,0 +1,69 @@
# Agent Note: Product one-shot subagents use generic background Tasks
Status: implemented
English | [中文](2026-08-12-product-subagent-one-shot-background-tasks.zh.md)
## Problem
The Codex and Claude Code providers already run one self-contained task and return one final answer, while `dsh-tool-subagent` already adapts any one-shot provider to the generic background Task runtime. The shipped product-tool rows disabled that route, so an agent could only wait for the product answer even when the delegation was independent of its next action.
Exposing background execution must not add a product session, product-specific task state, another cancellation owner, or another result protocol. The same provider run must remain responsible for one native process or query and one final answer, while the existing Task registry remains responsible for ids, collection, cancellation, owner cleanup, and completion notices.
## Decision
The `standard`, `code`, and `cordis` Agent Presets configure the dormant `subagent_codex` and `subagent_claude_code` rows with `backgroundMode: one-shot`. Removing a row's `disabled` field exposes the existing optional `run_in_background` argument. Omission or `false` waits in the foreground; explicit `true` returns a parent-owned Task id after synchronous Task preflight and registration, without waiting for provider startup or completion.
The [generic one-shot background adapter](2026-07-08-background-subagent-tasks.md) owns background registration and settlement. It starts the same [`SubagentRun`](2026-06-21-subagent-capability-seam.md), uses a Task-owned cancellation signal across provider startup and execution, waits for `run.result` and `run.dispose()`, maps the terminal result into the Task, and lets `task_output`, `task_list`, `task_kill`, and the existing completion notice expose that state. The [product provider decision](2026-08-04-claude-code-and-codex-subagent-backends.md) continues to own native protocols, answer selection, local cancellation, and process-tree quiescence.
No provider configuration, service interface, event, wire field, persistence format, or product identifier is added. Foreground and background differ only in which existing consumer waits for the same one-shot run.
### Ownership and lifecycle
```text
product tool call
-> omitted / false: tool call waits -> final answer or error -> run disposal
-> true: Task preflight + owner cleanup
-> starter begins provider startup under Task-owned signal
-> Task record/id published and returned (startup remains pending)
-> provider result + run disposal -> Task settlement + notice
-> task_output reads / task_kill cancels
-> parent disposal: Task owner cleanup cancels -> run disposal -> process exit
```
| Fact or resource | Owner | Product-tool responsibility | Observable result |
| --- | --- | --- | --- |
| Product selection and exposure | Agent Preset | Bind one fixed tool name to one fixed provider | Enabling one row exposes only that product tool |
| Foreground or background choice | `dsh-tool-subagent` | Resolve `run_in_background` under `one-shot` policy | Omission is foreground; explicit `true` returns a Task id |
| Task id, state, output, cancellation, and notice | `ctx.tasks` and `dsh-tool-tasks` | Register and present the existing one-shot run | Generic task tools collect or stop the run for the exact parent |
| Native answer and process quiescence | Product provider and `dsh-subprocess` | Produce one final result and release one process tree | Task settlement and foreground return both wait for disposal |
## Published composition
Full profiles keep both product providers on the host and keep both product-tool rows disabled in each full preset. The host task registry is shared across sessions, while each preset contributes the generic task controls to its own agent scope. A user copies a preset and removes `disabled` from either or both product rows; no product process starts during composition.
A custom composition that enables one-shot background execution must provide the complete generic Task capability: `dsh-tasks-local` as the provider and `dsh-tool-tasks` as the model-facing consumer. A product tool without that runtime can still execute in the foreground, but an explicit background request fails the existing Task preflight instead of publishing an uncollectable id.
The ACP product compositions use the same fixed product rows and generic task controls. Their keyless schema snapshots expose `description`, `prompt`, and optional `run_in_background` for each enabled product tool without invoking Codex, Claude Code, or an external model.
## Verification
The shipped Web composition boots four user-preset variants—neither product, Codex, Claude Code, and both—and checks that each enabled product tool exposes `run_in_background` alongside `task_output`, `task_list`, and `task_kill`. The two package-owned Loader compositions run with an empty `PATH`, inspect the same schemas and controls, and prove that loading the providers starts no product process. ACP keyless snapshots pin the assembled product schemas, while the existing `dsh-tool-subagent` and task suites pin foreground defaulting, Task registration, final-output collection, cancellation, completion notices, owner disposal, and provider disposal.
## Alternatives considered
**Keep the product tools foreground-only.** This preserves the smallest schema but prevents agents from scheduling independent product work even though the generic one-shot Task adapter already owns the required lifecycle.
**Make product delegations background by default.** A one-shot Task requires later collection, unlike a continuable child with its own durable conversation id and settlement delivery. Foreground remains the compatible default, and background remains an explicit scheduling choice.
**Use Codex or Claude Code native session state as the background owner.** That would create provider-specific ids, status, cancellation, and recovery semantics beside the generic Task registry. The providers remain one-shot result producers and keep native ids private.
**Add product-specific output, wait, or kill tools.** Separate controls would duplicate the generic task protocol and teach a different collection workflow for each provider. The existing `task_*` tools already cover the required operations.
**Add continuable product sessions at the same time.** Resume, follow-up, progress, and persisted product sessions require new product contracts and lifecycle ownership. This decision exposes only the already implemented one-shot background route.
## Consequences
Agents can continue useful work while Codex or Claude Code handles an independent one-shot task, then collect the final answer or cancel it through the same Task controls used by other background producers. Foreground callers retain their existing result and error behavior.
Every product delegation still starts a fresh native process or query, produces final text as its only product payload, and ends with provider disposal and whole-tree exit. A background call additionally exposes the generic Task id, status, completion notice, and collection or cancellation results. Background Tasks are process-local and parent-owned: they do not survive parent disposal, do not expose intermediate product activity, and do not make a product conversation resumable. Custom compositions that expose the background argument must also keep the generic Task provider and controls available.
@@ -0,0 +1,69 @@
# Agent Note: 产品 one-shot subagent 使用通用后台 Task
Status: implemented
[English](2026-08-12-product-subagent-one-shot-background-tasks.md) | 中文
## 问题
Codex 与 Claude Code 提供方已经能够运行一项自包含任务并返回一个最终回答,而 `dsh-tool-subagent` 也已经能够把任意 one-shot 提供方接入通用后台 Task 运行时。随附产品工具行禁用了这条路径,因此即使委托与 agent 的下一步操作彼此独立,agent 也只能等待产品回答。
公开后台执行不得增加产品会话、产品专属任务状态、另一取消责任方或另一结果协议。同一个提供方运行必须继续负责一个原生进程或 query 和一个最终回答,而现有任务注册表继续负责 id、收集、取消、owner 清理与完成通知。
## 决策
`standard``code``cordis` Agent Preset 使用 `backgroundMode: one-shot` 配置休眠的 `subagent_codex``subagent_claude_code` 行。删除某一行的 `disabled` 字段后,现有可选参数 `run_in_background` 就会公开。省略该参数或传入 `false` 时会在前台等待;显式传入 `true` 时会在同步完成 Task 预检与登记后返回由父级拥有的 Task id,而不会等待提供方启动或完成。
[通用 one-shot 后台适配器](2026-07-08-background-subagent-tasks.md)负责后台登记与结算。它会启动同一个 [`SubagentRun`](2026-06-21-subagent-capability-seam.md),让 Task 自有的取消信号覆盖提供方启动与执行,等待 `run.result``run.dispose()`,把终态结果映射进 Task,并由 `task_output``task_list``task_kill` 与现有完成通知公开该状态。[产品提供方决策](2026-08-04-claude-code-and-codex-subagent-backends.md)继续负责原生协议、答案选择、本地取消与进程树完全停稳。
本决策不新增提供方配置、服务接口、事件、协议字段、持久化格式或产品标识符。前台与后台的区别仅在于由哪个现有消费方等待同一个 one-shot 运行。
### 归属与生命周期
```text
product tool call
-> omitted / false: tool call waits -> final answer or error -> run disposal
-> true: Task preflight + owner cleanup
-> starter begins provider startup under Task-owned signal
-> Task record/id published and returned (startup remains pending)
-> provider result + run disposal -> Task settlement + notice
-> task_output reads / task_kill cancels
-> parent disposal: Task owner cleanup cancels -> run disposal -> process exit
```
| 事实或资源 | 责任方 | 产品工具职责 | 可观察结果 |
| --- | --- | --- | --- |
| 产品选择与公开 | Agent Preset | 把一个固定工具名绑定到一个固定提供方 | 启用一行只会公开对应产品工具 |
| 前台或后台选择 | `dsh-tool-subagent` | 按 `one-shot` 策略解析 `run_in_background` | 省略参数时在前台运行;显式传入 `true` 时返回 task id |
| task id、状态、输出、取消与通知 | `ctx.tasks``dsh-tool-tasks` | 登记并展示现有 one-shot 运行 | 通用任务工具为准确父级收集或停止运行 |
| 原生答案与进程完全停稳 | 产品提供方与 `dsh-subprocess` | 产生一个最终结果并释放一棵进程树 | Task 结算与前台返回都会等待资源释放 |
## 发布组装
完整 profile 把两个产品提供方保留在宿主,并在每个完整 preset 中让两个产品工具行保持禁用。宿主任务注册表由各会话共享,而每个 preset 会把通用任务控制工具贡献到自身 agent 作用域。用户复制一个 preset,再从任一或两个产品行删除 `disabled`;组装期间不会启动产品进程。
自定义组装若启用 one-shot 后台执行,就必须提供完整的通用 Task 能力:由 `dsh-tasks-local` 充当提供方,由 `dsh-tool-tasks` 充当面向模型的消费方。没有该运行时的产品工具仍可在前台执行,但显式后台请求会在现有 Task 预检中失败,不会发布无法收集的 id。
ACP 产品组装使用相同的固定产品行与通用任务控制工具。其无密钥 schema 快照会为每个已启用产品工具公开 `description``prompt` 和可选的 `run_in_background`,而不会调用 Codex、Claude Code 或外部模型。
## 验证
随附 Web 组装会启动四种用户 preset 变体——不启用产品、只启用 Codex、只启用 Claude Code,以及同时启用两者——并检查每个已启用产品工具都会与 `task_output``task_list``task_kill` 一起公开 `run_in_background`。两个由包负责的 Loader 组装会在空 `PATH` 下运行,检查相同 schema 与控制工具,并证明加载提供方不会启动产品进程。ACP 无密钥快照会固定组装后的产品 schema,而现有 `dsh-tool-subagent` 与任务测试套件会固定前台默认值、Task 登记、最终输出收集、取消、完成通知、owner 资源释放与提供方资源释放。
## 曾考虑的替代方案
**让产品工具继续只支持前台运行。** 这种方案保留最小 schema,却会阻止 agent 调度独立产品工作,即使通用 one-shot Task 适配器已经负责所需生命周期。
**让产品委托默认在后台运行。** one-shot Task 需要后续收集,这不同于拥有自身持久会话 id 与结算交付的可续接子级。前台继续作为兼容默认值,后台继续作为显式调度选择。
**让 Codex 或 Claude Code 原生会话状态负责后台生命周期。** 这会在通用任务注册表之外建立提供方专属 id、状态、取消与恢复语义。提供方继续只产生 one-shot 结果,并把原生 id 保持为私有事实。
**增加产品专属 output、wait 或 kill 工具。** 独立控制工具会复制通用任务协议,并为每个提供方教授不同的收集工作流。现有 `task_*` 工具已经覆盖所需操作。
**同时增加可续接产品会话。** 恢复、后续交互、进度与持久化产品会话需要新的产品约定和生命周期归属。本决策只公开已经实现的 one-shot 后台路径。
## 后果
agent 可以在 Codex 或 Claude Code 处理独立 one-shot 任务时继续推进其他工作,随后通过其他后台 producer 共用的 Task 控制工具收集最终回答或取消运行。前台调用方继续获得既有结果与错误行为。
每次产品委托仍会启动一个全新的原生进程或 query,把最终文本作为唯一产品载荷,并以提供方资源释放和整棵进程树退出结束。后台调用还会额外公开通用 Task id、状态、完成通知以及收集或取消结果。后台 Task 仅存在于当前进程且由父级拥有:它不会在父级资源释放后继续存活,不会公开产品中间活动,也不会让产品对话变得可恢复。公开后台参数的自定义组装还必须让通用 Task 提供方与控制工具保持可用。
@@ -207,7 +207,7 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -216,7 +216,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-workerthread
@@ -194,7 +194,7 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -203,7 +203,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-workerthread
@@ -134,7 +134,7 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -143,11 +143,11 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. The host must provide `codex` or `claude` on `PATH`; the preset does not install, authenticate, select a model for, or probe either product.
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. `backgroundMode: one-shot` keeps omitted or `false` calls in the foreground and lets explicit `run_in_background: true` return a generic Task id. Full presets already carry `tool-tasks`, while the host carries the task registry; retain both when making a custom composition so `task_output`, `task_list`, `task_kill`, cancellation, and completion notices stay available. The host must provide `codex` or `claude` on `PATH`; the preset does not install, authenticate, select a model for, or probe either product.
## What not to move into a preset
@@ -206,7 +206,7 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -215,7 +215,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-workerthread
+16
View File
@@ -122,6 +122,16 @@ async function bootWeb(settingsFile: string, extra: PatchOptions[] = []): Promis
const toolNames = (ctx: Context, agent?: Agent): string[] =>
ctx.tools.schemas(agent).map(schema => schema.name).sort()
function toolParameterNames(ctx: Context, agent: Agent, toolName: string): string[] {
const schema = ctx.tools.schemas(agent).find(tool => tool.name === toolName)
if (schema === undefined) throw new Error(`missing tool schema ${toolName}`)
const properties = schema.parameters.properties
if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {
throw new Error(`${toolName} has invalid parameter properties`)
}
return Object.keys(properties).sort()
}
function enablePresetTool(composition: string, id: string): string {
const row = ` - id: ${id}\n`
const start = composition.indexOf(row)
@@ -475,6 +485,12 @@ describe('product subagent rows in user presets', () => {
const tools = toolNames(productCtx, handle.agent)
expect(tools.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
.toEqual(productTools)
expect(tools).toEqual(expect.arrayContaining(['task_kill', 'task_list', 'task_output']))
for (const productTool of productTools) {
expect(toolParameterNames(productCtx, handle.agent, productTool)).toEqual([
'description', 'prompt', 'run_in_background',
])
}
} finally {
await handle.dispose()
}
+2 -1
View File
@@ -123,7 +123,8 @@
# Fork stays one-shot because a continuable child's `report` tool and prompt
# section precede the inherited history a fork reuses; `run_in_background` is off
# because this example mounts no task service. See .agents/notes/implemented/architecture/2026-08-10-fork-children-stay-one-shot.md.
# as an explicit foreground-only choice even though agent-spine-demo mounts the
# generic Task runtime. See .agents/notes/implemented/architecture/2026-08-10-fork-children-stay-one-shot.md.
- id: tool-subagent-fork
name: '@deepseek-ai/dsh-tool-subagent'
config:
@@ -27,12 +27,12 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
@@ -1,5 +1,5 @@
# Add both native product providers and the same independent foreground tool
# rows an Agent Preset may contribute. Loading the composition starts neither
# Add both native product providers and the same independent one-shot tool rows
# an Agent Preset may contribute. Loading the composition starts neither
# product; the scenario pins both model-visible schemas.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
@@ -16,12 +16,12 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
@@ -25,5 +25,5 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
@@ -1,4 +1,4 @@
# Add the native Codex product provider and its preset-shaped foreground tool to
# Add the native Codex product provider and its preset-shaped one-shot tool to
# the real ACP composition. The model is told not to call it; the scenario pins
# the assembled request schema without starting Codex.
- id: base
@@ -14,5 +14,5 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
@@ -1,4 +1,4 @@
# Test-only composition of both public opt-in providers and foreground tools.
# Test-only composition of both public opt-in providers and one-shot task tools.
# The owning e2e boots this tree but never invokes a model or product process.
- id: fixture
name: './fixture.ts'
@@ -20,7 +20,7 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: 'provider-managed'
- id: tool-subagent-claude-code
@@ -28,7 +28,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: 'provider-managed'
- id: agent-spine
@@ -53,11 +53,16 @@ try {
required: tool.parameters.required,
}
})
const taskTools = ctx.tools.schemas()
.map(schema => schema.name)
.filter(name => name === 'task_kill' || name === 'task_list' || name === 'task_output')
.sort()
process.stdout.write(`${JSON.stringify({
registeredProviders: ctx.subagents.list(),
providers,
tools,
taskTools,
starts,
})}\n`)
} finally {
@@ -1,4 +1,4 @@
# Test-only composition of the public opt-in provider and foreground tool.
# Test-only composition of the public opt-in provider and one-shot task tool.
# The owning e2e boots this tree but never invokes the model or Codex.
- id: fixture
name: './fixture.ts'
@@ -17,7 +17,7 @@
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: 'provider-managed'
- id: agent-spine
@@ -31,6 +31,10 @@ try {
if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {
throw new Error('subagent_codex tool has invalid parameter properties')
}
const taskTools = ctx.tools.schemas()
.map(schema => schema.name)
.filter(name => name === 'task_kill' || name === 'task_list' || name === 'task_output')
.sort()
process.stdout.write(`${JSON.stringify({
providers: ctx.subagents.list(),
@@ -44,6 +48,7 @@ try {
parameterNames: Object.keys(properties).sort(),
required: tool.parameters.required,
},
taskTools,
starts,
})}\n`)
} finally {
@@ -256,7 +256,7 @@
},
{
"name": "subagent_claude_code",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the subagent and returns its result.",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the result by default. Set `run_in_background: true` to return a task id; collect with `task_output` and stop with `task_kill`.",
"parameters": {
"type": "object",
"properties": {
@@ -267,6 +267,10 @@
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run as a background task and return its id. Defaults to false; collect with task_output or stop with task_kill."
}
},
"required": [
@@ -277,7 +281,7 @@
},
{
"name": "subagent_codex",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the subagent and returns its result.",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the result by default. Set `run_in_background: true` to return a task id; collect with `task_output` and stop with `task_kill`.",
"parameters": {
"type": "object",
"properties": {
@@ -288,6 +292,10 @@
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run as a background task and return its id. Defaults to false; collect with task_output or stop with task_kill."
}
},
"required": [
@@ -256,7 +256,7 @@
},
{
"name": "subagent_codex",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the subagent and returns its result.",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. This call waits for the result by default. Set `run_in_background: true` to return a task id; collect with `task_output` and stop with `task_kill`.",
"parameters": {
"type": "object",
"properties": {
@@ -267,6 +267,10 @@
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run as a background task and return its id. Defaults to false; collect with task_output or stop with task_kill."
}
},
"required": [
+2 -1
View File
@@ -120,7 +120,8 @@
# Fork stays one-shot because a continuable child's `report` tool and prompt
# section precede the inherited history a fork reuses; `run_in_background` is off
# because this example mounts no task service. See .agents/notes/implemented/architecture/2026-08-10-fork-children-stay-one-shot.md.
# as an explicit foreground-only choice even though agent-spine-demo mounts the
# generic Task runtime. See .agents/notes/implemented/architecture/2026-08-10-fork-children-stay-one-shot.md.
- id: tool-subagent-fork
name: '@deepseek-ai/dsh-tool-subagent'
config:
@@ -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-claude-code/README.md
README.md: 17b14e847baea3eadda7129b5e49f5e65b668cc8
README.zh.md: 2f59144d5bd9f26a58773e6dd53909b2b0e8da14
README.md: 556cde7da4467659d02709c43325e60fa7de6a74
README.zh.md: bda00d7be4467e739604eeba0020850cb1a99d50
@@ -31,7 +31,7 @@ The provider advertises no optional start-time capabilities and reports `inherit
Production resolves `claude` from the subprocess execution world's credential-scrubbed `PATH`, with explicit `env` entries applied, and passes the resulting path to the SDK as `pathToClaudeCodeExecutable`. On Windows, a resolved `.cmd` or `.bat` path is carried as a quoted, per-spawn environment value that `cmd.exe /v:off` expands once, so valid path metacharacters remain data. The pinned SDK's fixed flags then occupy cmd's command tail and contain no cmd metacharacters; they are not ordinary Windows argv. Native settings and authentication remain authoritative. The plugin does not install another CLI, select a model, create a product home, log in, or probe an account. Credential-shaped ambient variables are removed before the explicit `env` overlay is applied, so an API key or token intended for the child must be supplied there. Non-credential endpoint variables such as `ANTHROPIC_BASE_URL`, along with ordinary ambient values such as `PATH` and `HOME`, remain inherited unless overridden.
Shipped profiles load this provider once on the host and start no Claude process until a tool call. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_claude_code` only to agents composed from the copy. A custom host composition can still use both rows directly.
Shipped profiles load this provider once on the host and start no Claude process until a tool call. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_claude_code` only to agents composed from the copy. Its `one-shot` policy keeps omitted or `false` `run_in_background` calls in the foreground, while explicit `true` returns a parent-owned Task id for `task_output` or `task_kill`. Shipped full profiles already provide the task registry and controls; a custom composition must load the same generic task provider and consumer when it enables this background route.
```yaml
- id: subagent-claude-code
@@ -40,13 +40,19 @@ Shipped profiles load this provider once on the host and start no Claude process
env:
ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
- id: tasks
name: '@deepseek-ai/dsh-tasks-local'
- id: tool-tasks
name: '@deepseek-ai/dsh-tool-tasks'
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
@@ -72,19 +78,19 @@ The child pays for an independent Claude Code context and query. Child tokens do
Independent of the parent request cache. Reuse depends only on Claude Code's own model, instructions, tools, native settings, and fresh query.
### Parent tool result, indirectly
### Parent scheduling and results, indirectly
#### What the model sees
Through `dsh-tool-subagent`, the parent sees only the strict final Claude Code answer or the consumer's exact error for a non-completed result. Claude Code reasoning, tool activity, intermediate messages, stderr, workspace diffs, usage, and product ids are not copied into the parent Session.
Through `dsh-tool-subagent`, a foreground call gives the parent the strict final Claude Code answer or the consumer's exact error for a non-completed result. A background call first returns a Task id; the generic task controls later deliver a bounded completion notice, expose the final answer and status through `task_output`, and let `task_kill` request cancellation. Claude Code reasoning, tool activity, intermediate messages, stderr, workspace diffs, usage, and product ids are not copied into the parent Session.
#### Token effect
Parent input grows only by the final answer or error retained in the tool result. This provider adds no parent tool schema by itself.
Foreground input grows by the retained final answer or error. Background input also includes the start acknowledgement, completion notice, and any `task_output`, `task_kill`, or later status results; child tokens still do not enter the parent context. This provider adds no parent tool schema by itself.
#### KV Cache effect
Append-only: the new tool result follows the reusable parent request prefix.
Append-only: foreground adds one result after the reusable parent prefix, while background appends the Task acknowledgement, notice, and later control or collection results. Background scheduling can add a notice-driven turn, but none of these messages rewrites the earlier prefix.
## Known Limitations and Deferred Work
@@ -93,6 +99,6 @@ Append-only: the new tool result follows the reusable parent request prefix.
- **Product installation and account state remain native** — a missing or incompatible `claude`, configuration error, or authentication failure is surfaced as a startup or run error; the plugin provides no installer or login flow.
- **The SDK platform CLI remains in the install closure** — production ignores it in favor of the host `claude`, but the current SDK optional dependency is still installed and supplies the keyless compatibility fixture. Removing that payload belongs to the separate product installation-closure follow-up.
- **No human interaction path** — `AskUserQuestion` is disabled and other interactive callbacks are absent, so tasks requiring new approval or input fail instead of suspending.
- **Final text only** — reasoning, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local.
- **Product payload is final text only** — reasoning, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local; generic Task ids, notices, and status come from the shared task runtime.
- **No optional shared capabilities** — output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider.
- **No wall-clock timeout or side-effect rollback** — the caller cancels long work, and files or external systems changed before cancellation are not restored.
@@ -31,7 +31,7 @@ SDK 接收由文本块原样拼接成的任务。提供方会完整迭代 SDK
生产环境从子进程执行世界清除凭证后的 `PATH` 解析 `claude`,再应用显式 `env` 条目,并把所得路径作为 `pathToClaudeCodeExecutable` 交给 SDK。在 Windows 上,解析到的 `.cmd``.bat` 路径会作为带引号、仅供本次 spawn 使用的环境值交给 `cmd.exe /v:off` 展开一次,因此合法路径中的元字符仍只是数据。锁定版本的 SDK 随后把固定命令行选项放在 cmd 的命令尾部;这些选项不含 cmd 元字符,也并不是普通的 Windows argv。原生设置与身份验证继续是权威来源。本插件不安装另一份 CLI、不选择模型、不创建产品主目录、不执行登录,也不探测账户。具有凭证特征的环境变量会在显式 `env` 覆盖生效前被清除,因此供子进程使用的 API 密钥或 token 必须在该配置中显式提供。除非被覆盖,`ANTHROPIC_BASE_URL` 等非凭证端点变量以及 `PATH``HOME` 等普通环境变量仍会被继承。
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Claude 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_claude_code`自定义宿主组装仍可直接使用两条配置行
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Claude 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_claude_code``one-shot` 策略会让省略 `run_in_background` 或传入 `false` 的调用继续在前台等待,而显式传入 `true` 会返回由父 agent 拥有的 Task ID,供 `task_output``task_kill` 使用。随附完整 profile 已提供任务注册表和控制工具;自定义组装若启用该后台路径,也必须加载同一通用任务提供方与消费方
```yaml
- id: subagent-claude-code
@@ -40,13 +40,19 @@ SDK 接收由文本块原样拼接成的任务。提供方会完整迭代 SDK
env:
ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
- id: tasks
name: '@deepseek-ai/dsh-tasks-local'
- id: tool-tasks
name: '@deepseek-ai/dsh-tool-tasks'
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
@@ -72,19 +78,19 @@ Claude Code 子任务会在一个全新的 SDK query 中接收独立文本任务
这与父请求缓存相互独立。能否复用只取决于 Claude Code 自身的模型、指令、工具、原生设置和全新 query。
### 父级工具结果(间接)
### 父级调度与结果(间接)
#### 模型看到的内容
通过 `dsh-tool-subagent`,父级模型只会看到符合严格成功条件的 Claude Code 最终答案,或者在结果未完成时看到消费方给出的原样错误。Claude Code 的推理、工具活动、中间消息、stderr、工作区差异、用量信息和产品标识符均不会复制到父会话。
通过 `dsh-tool-subagent`前台调用会让父级模型看到符合严格成功条件的 Claude Code 最终答案,或者在结果未完成时看到消费方给出的原样错误。后台调用会先返回 Task id;随后通用任务控制面会送达有界完成通知,通过 `task_output` 公开最终答案与状态,并允许 `task_kill` 请求取消。Claude Code 的推理、工具活动、中间消息、stderr、工作区差异、用量信息和产品标识符均不会复制到父会话。
#### 对 token 的影响
父级输入会增加工具结果中保留的最终答案或错误内容。本提供方自身不添加父级工具 schema。
前台输入会增加工具结果中保留的最终答案或错误内容。后台输入还会包含启动确认、完成通知,以及 `task_output``task_kill` 或后续状态结果;子任务 token 仍不会进入父级上下文。本提供方自身不添加父级工具 schema。
#### 对 KV Cache 的影响
仅追加:新的工具结果接在可复用的父请求前缀后。
仅追加:前台会在可复用的父请求前缀后增加一个结果,后台则会继续追加 Task 启动确认、通知以及后续控制或收集结果。后台调度可能增加一个由通知唤醒的轮次,但这些消息都不会改写更早的前缀
## 已知限制与后续工作
@@ -93,6 +99,6 @@ Claude Code 子任务会在一个全新的 SDK query 中接收独立文本任务
- **产品安装与账户状态仍由原生机制管理**:`claude` 缺失或不兼容、配置错误或身份验证失败都会呈现为启动错误或运行错误;本插件不提供安装程序或登录流程。
- **SDK 平台 CLI 仍在安装闭包内**:生产环境会忽略它,改用宿主提供的 `claude`,但当前 SDK 的可选依赖仍会安装,并提供无密钥兼容性 fixture。移除该载荷属于独立的产品安装闭包后续项。
- **没有人工交互路径**`AskUserQuestion` 被禁用,其他交互回调也不存在,因此需要新审批或输入的任务会失败而不会挂起。
- **仅返回最终文本**:推理、中间消息、工具通信、用量信息、stderr 和工作区差异仍只保留在产品内部。
- **产品载荷仅包含最终文本**:推理、中间消息、工具通信、用量信息、stderr 和工作区差异仍只保留在产品内部;通用 Task id、通知与状态来自共享任务运行时
- **没有可选的共享能力**:对于本提供方,共享服务会拒绝输出 schema、子任务角色设定、工具筛选和 harness 深度强制约束。
- **没有按实际经过时间触发的超时或副作用回滚**:长时间运行的工作由调用方取消,且取消前已更改的文件或外部系统不会恢复原状。
@@ -15,7 +15,7 @@ const configPath = join(fixtureDir, 'cordis.yml')
const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
describe('product-provider public Loader composition', () => {
it('loads both opt-in packages and foreground tools without starting either product', async () => {
it('loads both opt-in packages, one-shot task tools, and task controls without starting either product', async () => {
const { stdout, stderr } = await runLoaderSmoke({
label: 'product-provider Loader composition',
tempDirPrefix: 'dsh-product-provider-loader-',
@@ -57,15 +57,16 @@ describe('product-provider public Loader composition', () => {
tools: [
{
name: 'subagent_codex',
parameterNames: ['description', 'prompt'],
parameterNames: ['description', 'prompt', 'run_in_background'],
required: ['description', 'prompt'],
},
{
name: 'subagent_claude_code',
parameterNames: ['description', 'prompt'],
parameterNames: ['description', 'prompt', 'run_in_background'],
required: ['description', 'prompt'],
},
],
taskTools: ['task_kill', 'task_list', 'task_output'],
starts: 0,
})
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
@@ -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-codex/README.md
README.md: 3d59ca1eaf3db9dd9d9d2cd451692ebd2a956ef4
README.zh.md: b60cb1bba9b2d7b3f61c544c1600862a0ad6ce5b
README.md: 6a882c128ebd372ee148a4315a237d4eacf6d42b
README.zh.md: beb5411f6a449efc7a45c171d217f98657805740
+13 -7
View File
@@ -27,7 +27,7 @@ The provider advertises no optional start-time capabilities and reports `inherit
Production resolves `codex` from `PATH` and uses the host's native Codex configuration and authentication. The plugin does not install Codex, select a model, create `CODEX_HOME`, log in, or probe a version. Credential-shaped ambient variables are removed by the subprocess seam, so an API key intended for the child must be supplied explicitly in `env`; ordinary ambient values such as `PATH` and `HOME` remain available unless overridden.
Shipped profiles load this provider once on the host and start no Codex process until a tool call. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_codex` only to agents composed from the copy. A custom host composition can still use both rows directly.
Shipped profiles load this provider once on the host and start no Codex process until a tool call. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_codex` only to agents composed from the copy. Its `one-shot` policy keeps omitted or `false` `run_in_background` calls in the foreground, while explicit `true` returns a parent-owned Task id for `task_output` or `task_kill`. Shipped full profiles already provide the task registry and controls; a custom composition must load the same generic task provider and consumer when it enables this background route.
```yaml
- id: subagent-codex
@@ -36,13 +36,19 @@ Shipped profiles load this provider once on the host and start no Codex process
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
- id: tasks
name: '@deepseek-ai/dsh-tasks-local'
- id: tool-tasks
name: '@deepseek-ai/dsh-tool-tasks'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
@@ -66,19 +72,19 @@ The child pays for an independent Codex context and turn. Child tokens do not en
Independent of the parent request cache. Reuse depends only on Codex's own provider, model, instructions, tools, and ephemeral-thread request.
### Parent tool result, indirectly
### Parent scheduling and results, indirectly
#### What the model sees
Through `dsh-tool-subagent`, the parent sees only the selected final Codex answer or the consumer's exact error for a non-completed result. Codex commentary, reasoning, tool activity, stderr, workspace diffs, and product ids are not copied into the parent Session.
Through `dsh-tool-subagent`, a foreground call gives the parent the selected final Codex answer or the consumer's exact error for a non-completed result. A background call first returns a Task id; the generic task controls later deliver a bounded completion notice, expose the final answer and status through `task_output`, and let `task_kill` request cancellation. Codex commentary, reasoning, tool activity, stderr, workspace diffs, usage, and product ids are not copied into the parent Session.
#### Token effect
Parent input grows only by the final answer or error retained in the tool result. This provider adds no parent tool schema by itself.
Foreground input grows by the retained final answer or error. Background input also includes the start acknowledgement, completion notice, and any `task_output`, `task_kill`, or later status results; child tokens still do not enter the parent context. This provider adds no parent tool schema by itself.
#### KV Cache effect
Append-only: the new tool result follows the reusable parent request prefix.
Append-only: foreground adds one result after the reusable parent prefix, while background appends the Task acknowledgement, notice, and later control or collection results. Background scheduling can add a notice-driven turn, but none of these messages rewrites the earlier prefix.
## Known Limitations and Deferred Work
@@ -86,6 +92,6 @@ Append-only: the new tool result follows the reusable parent request prefix.
- **Host-managed product installation and account state** — a missing or incompatible `codex`, configuration error, or authentication failure is surfaced as a startup or run error; the plugin provides no installer, login flow, or runtime version gate.
- **Compatibility is pinned by development evidence** — upgrading from the verified 0.147.0 protocol baseline requires regenerating upstream schema evidence and rerunning handshake, answer-selection, approval, cancellation, keyless real-product, and credentialed DeepSeek nonce tests.
- **No human approval path** — known unattended approval requests are denied and unknown server requests fail closed; deployments cannot configure an allow policy through this package.
- **Final text only** — reasoning, commentary, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local.
- **Product payload is final text only** — reasoning, commentary, intermediate messages, tool traffic, usage, stderr, and workspace diffs remain product-local; generic Task ids, notices, and status come from the shared task runtime.
- **No optional shared capabilities** — output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider.
- **No wall-clock timeout or side-effect rollback** — the caller cancels long work, and files or external systems changed before cancellation are not restored.
+13 -7
View File
@@ -27,7 +27,7 @@
生产环境会从 `PATH` 中解析 `codex`,并使用宿主机原生的 Codex 配置与身份验证。本插件不安装 Codex、不选择模型、不创建 `CODEX_HOME`、不执行登录,也不探测版本。子进程 seam 会移除具有凭证特征的环境变量,因此供子进程使用的 API 密钥必须在 `env` 中显式提供;除非被覆盖,`PATH``HOME` 等普通环境变量值仍然可用。
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Codex 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_codex`自定义宿主组装仍可直接使用两条配置行
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Codex 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_codex``one-shot` 策略会让省略 `run_in_background` 或传入 `false` 的调用继续在前台等待,而显式传入 `true` 会返回由父 agent 拥有的 Task ID,供 `task_output``task_kill` 使用。随附完整 profile 已提供任务注册表和控制工具;自定义组装若启用该后台路径,也必须加载同一通用任务提供方与消费方
```yaml
- id: subagent-codex
@@ -36,13 +36,19 @@
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
- id: tasks
name: '@deepseek-ai/dsh-tasks-local'
- id: tool-tasks
name: '@deepseek-ai/dsh-tool-tasks'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
@@ -66,19 +72,19 @@ Codex 子任务会在一个全新的临时线程中,以单个轮次接收这
这与父请求缓存相互独立。能否复用只取决于 Codex 自身的提供方、模型、指令、工具和临时线程请求。
### 父级工具结果(间接)
### 父级调度与结果(间接)
#### 模型看到的内容
通过 `dsh-tool-subagent`,父级模型只会看到选定的 Codex 最终答案,或者在结果未完成时看到消费方给出的原样错误。Codex 的过程说明、推理(reasoning)、工具活动、stderr、工作区差异和产品标识符均不会复制到父会话。
通过 `dsh-tool-subagent`前台调用会让父级模型看到选定的 Codex 最终答案,或者在结果未完成时看到消费方给出的原样错误。后台调用会先返回 Task id;随后通用任务控制面会送达有界完成通知,通过 `task_output` 公开最终答案与状态,并允许 `task_kill` 请求取消。Codex 的过程说明、推理(reasoning)、工具活动、stderr、工作区差异、用量信息和产品标识符均不会复制到父会话。
#### 对 token 的影响
父级输入会增加工具结果中保留的最终答案或错误内容。本提供方自身不添加父级工具 schema。
前台输入会增加工具结果中保留的最终答案或错误内容。后台输入还会包含启动确认、完成通知,以及 `task_output``task_kill` 或后续状态结果;子任务 token 仍不会进入父级上下文。本提供方自身不添加父级工具 schema。
#### 对 KV Cache 的影响
仅追加:新的工具结果接在可复用的父请求前缀后。
仅追加:前台会在可复用的父请求前缀后增加一个结果,后台则会继续追加 Task 启动确认、通知以及后续控制或收集结果。后台调度可能增加一个由通知唤醒的轮次,但这些消息都不会改写更早的前缀
## 已知限制与后续工作
@@ -86,6 +92,6 @@ Codex 子任务会在一个全新的临时线程中,以单个轮次接收这
- **产品安装和账户状态由宿主管理**:`codex` 缺失或不兼容、配置错误或身份验证失败,都会呈现为启动错误或运行错误;本插件不提供安装程序、登录流程或运行时版本门禁。
- **兼容性由开发证据锁定**:若要从已验证的 0.147.0 协议基线升级,必须重新生成上游 schema 证据,并重新运行握手、答案选择、审批、取消、无密钥真实产品以及带密钥的 DeepSeek 随机数测试。
- **没有人工审批路径**:已知的无人值守审批请求会被拒绝,未知服务器请求会以默认拒绝方式使运行失败;部署方无法通过本包配置允许策略。
- **仅返回最终文本**:推理、过程说明、中间消息、工具通信、用量信息、stderr 和工作区差异仍只保留在产品内部。
- **产品载荷仅包含最终文本**:推理、过程说明、中间消息、工具通信、用量信息、stderr 和工作区差异仍只保留在产品内部;通用 Task id、通知与状态来自共享任务运行时
- **没有可选的共享能力**:对于本提供方,共享服务会拒绝输出 schema、子任务角色设定、工具筛选和 harness 深度强制约束。
- **没有按实际经过时间触发的超时或副作用回滚**:长时间运行的工作由调用方取消,且取消前已更改的文件或外部系统不会恢复原状。
@@ -15,7 +15,7 @@ const configPath = join(fixtureDir, 'cordis.yml')
const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
describe('Codex provider public Loader composition', () => {
it('loads the opt-in package and foreground tool without starting Codex', async () => {
it('loads the opt-in package, one-shot task tool, and task controls without starting Codex', async () => {
const { stdout, stderr } = await runLoaderSmoke({
label: 'subagent-codex Loader composition',
tempDirPrefix: 'dsh-subagent-codex-loader-',
@@ -44,9 +44,10 @@ describe('Codex provider public Loader composition', () => {
},
tool: {
name: 'subagent_codex',
parameterNames: ['description', 'prompt'],
parameterNames: ['description', 'prompt', 'run_in_background'],
required: ['description', 'prompt'],
},
taskTools: ['task_kill', 'task_list', 'task_output'],
starts: 0,
})
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
@@ -85,7 +85,11 @@ describe('dsh-tool-subagent', () => {
it('registers a `subagent` tool that delegates to the configured provider and returns its output', async () => {
const ctx = await setup({ provider: 'mock' }, { reply: 'child says hi' })
const result = await callSubagent(ctx, { description: 'do a thing', prompt: 'go research X' })
const result = await callSubagent(ctx, {
description: 'do a thing',
prompt: 'go research X',
run_in_background: false,
})
expect(result.isError).toBe(false)
if (result.isError) throw new Error('expected subagent success')
expect(result.value).toEqual({