feat(subagent): make Claude Code provider directly installable

This commit is contained in:
pku-xht
2026-08-14 16:05:48 +08:00
parent 61ef3d8423
commit b4366e711d
48 changed files with 171 additions and 232 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-product-subagent-providers-in-shared-host.md
2026-08-10-product-subagent-providers-in-shared-host.md: 2b1a417f7e751b2edee7e3b23dd439feab1353ea
2026-08-10-product-subagent-providers-in-shared-host.zh.md: 2f687b7dc9332680b8aa610f46668f197066c517
2026-08-10-product-subagent-providers-in-shared-host.md: 564fd315c41c2f6999eed65bd7241e8b7167f43e
2026-08-10-product-subagent-providers-in-shared-host.zh.md: eaa24bb323191b14edbd2f756033b700374f5356
@@ -6,21 +6,21 @@ English | [中文](2026-08-10-product-subagent-providers-in-shared-host.zh.md)
## Problem
The [Codex and Claude Code provider contracts](../feature/2026-08-04-claude-code-and-codex-subagent-backends.md) are independently installable packages loaded beside the common subagent tool. Agent Presets are the ordinary owner of one agent's model-visible tools, but a preset cannot safely own these product providers: `ctx.subagents` is a process registry, provider names are unique, and host consumers resolve the same registry across sessions. Bundle installation and Preset tool grant are therefore separate deployment and agent-authoring decisions.
The [Codex and Claude Code provider contracts](../feature/2026-08-04-claude-code-and-codex-subagent-backends.md) are separate packages loaded beside the common subagent tool. The Claude Code package is directly installable as a Profile Bundle, while a deployment mounts the Codex package explicitly. Agent Presets are the ordinary owner of one agent's model-visible tools, but a preset cannot safely own either provider: `ctx.subagents` is a process registry, provider names are unique, and host consumers resolve the same registry across sessions. Host availability and Preset tool grants are therefore separate deployment and agent-authoring decisions.
The placement decision must preserve two independent facts. Loading a provider must not start or authenticate a product, while granting a tool must remain per preset so two sessions can expose different products. A global product switch, a provider instance per agent, or pre-enumerated combination presets would each create a second owner for one of those facts.
## Decision
When installed in a Profile, each product Bundle loads its fixed `codex` or `claude-code` provider exactly once in the shared Host plane. Loading either plugin only registers a dormant backend; the corresponding Codex or Claude process starts on the first actual delegation call. Agent Presets independently contribute ordinary `dsh-tool-subagent` rows for `subagent_codex` and `subagent_claude_code`, so a preset can grant neither tool, either one, or both without changing the provider registry. A tool whose provider Bundle is not installed remains unavailable rather than mounting another provider in the Agent plane.
The Claude Code Bundle and an explicit Codex Host row each load their fixed provider exactly once in the shared Host plane. Loading either plugin only registers a dormant backend; the corresponding Codex or Claude process starts on the first actual delegation call. Agent Presets independently contribute ordinary `dsh-tool-subagent` rows for `subagent_codex` and `subagent_claude_code`, so a preset can grant neither tool, either one, or both without changing the provider registry. A tool whose provider is absent remains unavailable rather than mounting another provider in the Agent plane.
The [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md) partially supersedes only this note's former default-inclusion choice: the base bundle excludes both providers, and each provider package owns its directly installable Bundle patch. This note continues to own process-wide Host placement whenever a product Bundle is installed. The provider-contract note continues to own each product protocol, result mapping, cancellation, process-tree lifecycle, and evidence tiers. The [Agent Preset architecture](2026-08-03-per-session-agent-presets.md) continues to own the Host/Agent split, preset authoring, and the rule that edits affect only newly composed sessions.
The [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md) partially supersedes only this note's former default-inclusion choice: the base bundle excludes both providers, the Claude Code package owns a directly installable Bundle patch, and Codex remains an explicitly mounted Host plugin. This note continues to own process-wide Host placement whenever either provider is present. The provider-contract note continues to own each product protocol, result mapping, cancellation, process-tree lifecycle, and evidence tiers. The [Agent Preset architecture](2026-08-03-per-session-agent-presets.md) continues to own the Host/Agent split, preset authoring, and the rule that edits affect only newly composed sessions.
The providers have different executable owners. Codex starts a host `codex` from `PATH`. The Claude Code Bundle installs its pinned Agent SDK and matching platform CLI; the provider lets that SDK choose the private native executable and passes the command through the shared subprocess owner without consulting or falling back to a host `claude`. Loading either Bundle only registers the provider and creates no product state, probes no version or authentication, and adds no product-specific setting. A missing Codex command or Claude platform payload, authentication failure, and other product failures remain local to the attempted delegation.
The providers have different executable owners. Codex starts a host `codex` from `PATH`. The Claude Code Bundle installs its pinned Agent SDK and matching platform CLI; the provider lets that SDK choose the private native executable and passes the command through the shared subprocess owner without consulting or falling back to a host `claude`. Loading either provider only registers it and creates no product state, probes no version or authentication, and adds no product-specific setting. A missing Codex command or Claude platform payload, authentication failure, and other product failures remain local to the attempted delegation.
## Verification
Real composition loads the selected set of no product Bundle, Codex only, Claude Code only, or both, and crosses it with Agent Presets that grant none, either, or both tools. It proves the Host registry equals the installed Bundle set, model-visible tools equal the installed-and-granted intersection, and no product process starts during composition. Preset edit coverage retains generation isolation. Keyless ACP snapshots pin the model-visible tool schemas, while provider tests separately prove host executable resolution for Codex, SDK platform-payload selection without fallback for Claude Code, failure, cancellation, and process-tree quiescence.
Real composition loads either no Claude Code Bundle or the Claude Code Bundle and crosses that availability with Agent Presets that leave its tool disabled or grant it. It proves the Host registry and model-visible tools reflect those two decisions, no product process starts during composition, and Preset edits affect only later Sessions. Existing Codex Loader and provider tests separately prove explicit Host composition and host executable resolution. Keyless ACP snapshots pin the model-visible tool schemas, while provider tests prove SDK platform-payload selection without fallback for Claude Code, failure, cancellation, and process-tree quiescence.
## Alternatives considered
@@ -34,6 +34,6 @@ Real composition loads the selected set of no product Bundle, Codex only, Claude
## Consequences
A user installs only the product Bundles available to a Profile and manages model-visible grants through the same Agent Preset authoring path as other plugins. Each new session receives the intersection of its preset's tool rows and the Profile's installed providers. An installed but ungranted product remains dormant and consumes its package and module-loading footprint but no product process, login, model call, or product home; an uninstalled product contributes no provider or SDK closure.
A user installs the Claude Code Bundle only in Profiles that need it, while a deployment that uses Codex mounts that Host plugin explicitly. Model-visible grants use the same Agent Preset authoring path as other plugins. Each new Session receives the intersection of its preset's tool rows and the Host's available providers. A present but ungranted product remains dormant and consumes its package and module-loading footprint but no product process, login, model call, or product home; an absent product contributes no provider closure.
The Host registry remains the single provider authority, each Bundle remains the deployment availability authority, and each Preset remains the model-tool authority. This explicit two-gate lifecycle avoids a global enable switch and keeps package removal independent from per-session authoring.
The Host registry remains the single provider authority, the Profile Bundle or explicit Host composition remains the deployment availability authority, and each Preset remains the model-tool authority. This explicit two-gate lifecycle avoids a global enable switch and keeps package removal independent from per-session authoring.
@@ -6,21 +6,21 @@ Status: implemented
## 问题
[Codex 与 Claude Code 提供方约定](../feature/2026-08-04-claude-code-and-codex-subagent-backends.md)是可独立安装的包,由部署环境在通用 subagent 工具旁加载。Agent Preset 是单个 agent(智能体)的模型可见工具的常规责任方,但 preset 不能安全地拥有这些产品提供方:`ctx.subagents` 是进程级注册表,提供方名称唯一,而宿主消费方会跨会话解析同一个注册表。因此,Bundle 安装与 Preset 工具授权分别属于部署决策和 agent 创作决策。
[Codex 与 Claude Code 提供方约定](../feature/2026-08-04-claude-code-and-codex-subagent-backends.md)由两个独立包实现,并在通用 subagent 工具旁加载。Claude Code 包可作为 Profile Bundle 直接安装,而部署环境会显式挂载 Codex 包。Agent Preset 是单个 agent(智能体)的模型可见工具的常规责任方,但 preset 不能安全地拥有任一产品提供方:`ctx.subagents` 是进程级注册表,提供方名称唯一,而宿主消费方会跨会话解析同一个注册表。因此,Host 可用性与 Preset 工具授权分别属于部署决策和 agent 创作决策。
归属决策必须同时保留两个彼此独立的事实:加载提供方不得启动产品,也不得对产品执行身份验证;而工具授权仍须按 preset 决定,这样两个会话才能暴露不同的产品。全局产品开关、按 agent 创建提供方实例或预先枚举的组合 preset,都会为其中一个事实另设第二责任方。
## 决策
产品 Bundle 安装到 Profile 后,会在共享 Host 平面中恰好加载一次固定的 `codex``claude-code` 提供方。加载任一插件只会注册一个休眠后端;对应的 Codex 或 Claude 进程直到第一次实际委派调用时才启动。Agent Preset 分别通过普通的 `dsh-tool-subagent` 行贡献 `subagent_codex``subagent_claude_code`,因此一个 preset 可以不授权任何工具、只授权其中一个或同时授权两者,而无需更改提供方注册表。若工具对应的提供方 Bundle 尚未安装,该工具仍不可用,而不会在 Agent 平面中另行挂载提供方。
Claude Code Bundle 与显式 Codex Host 行都会在共享 Host 平面中恰好加载一次各自固定的提供方。加载任一插件只会注册一个休眠后端;对应的 Codex 或 Claude 进程直到第一次实际委派调用时才启动。Agent Preset 分别通过普通的 `dsh-tool-subagent` 行贡献 `subagent_codex``subagent_claude_code`,因此一个 preset 可以不授权任何工具、只授权其中一个或同时授权两者,而无需更改提供方注册表。若工具对应的提供方不存在,该工具仍不可用,而不会在 Agent 平面中另行挂载提供方。
[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)只部分取代本说明先前关于默认包含提供方的选择:base 组合包排除两个提供方,每个提供方包负责其可直接安装的 Bundle patch。本说明继续负责产品 Bundle 安装后进程级 Host 放置。提供方约定说明继续负责每个产品的协议、结果映射、取消、进程树生命周期与证据层级。[Agent Preset 架构](2026-08-03-per-session-agent-presets.md)继续负责宿主与 agent 的划分、preset 创作,以及改动只影响新组装会话的规则。
[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)只部分取代本说明先前关于默认包含提供方的选择:base 组合包排除两个提供方,Claude Code 包拥有可直接安装的 Bundle patch,而 Codex 仍是显式挂载的 Host 插件。本说明继续负责任一提供方存在时的进程级 Host 放置。提供方约定说明继续负责每个产品的协议、结果映射、取消、进程树生命周期与证据层级。[Agent Preset 架构](2026-08-03-per-session-agent-presets.md)继续负责宿主与 agent 的划分、preset 创作,以及改动只影响新组装会话的规则。
两个提供方的可执行文件归属不同。Codex 会启动从 `PATH` 解析出的宿主 `codex`。Claude Code Bundle 会安装锁定的 Agent SDK 与匹配平台 CLI;提供方让 SDK 选择该私有原生可执行文件,再把命令交给共享子进程责任方,既不查询也不回退宿主 `claude`。加载任一 Bundle 只会注册提供方,不会创建产品状态、探测版本或身份验证,也不会新增产品专属设置。Codex 命令缺失、Claude 平台载荷缺失、身份验证失败和其他产品故障仍局限于发生问题的那次委派。
两个提供方的可执行文件归属不同。Codex 会启动从 `PATH` 解析出的宿主 `codex`。Claude Code Bundle 会安装锁定的 Agent SDK 与匹配平台 CLI;提供方让 SDK 选择该私有原生可执行文件,再把命令交给共享子进程责任方,既不查询也不回退宿主 `claude`。加载任一提供方只会完成注册,不会创建产品状态、探测版本或身份验证,也不会新增产品专属设置。Codex 命令缺失、Claude 平台载荷缺失、身份验证失败和其他产品故障仍局限于发生问题的那次委派。
## 验证
真实组装会加载未安装产品 Bundle、仅安装 Codex、仅安装 Claude Code 或两者都安装这四种集合,并与不授权工具、仅授权其中一个或同时授权两者的 Agent Preset 完整交叉。测试证明 Host 注册表等于已安装 Bundle 集合,模型可见工具等于已安装且已授权集合的交集,并且组装期间不会启动产品进程Preset 编辑覆盖继续证明代际隔离。无密钥 ACPAgent Client Protocol)快照固定模型可见工具 schema,提供方测试则分别证明 Codex 的宿主可执行文件解析、Claude Code 的 SDK 平台载荷选择与无回退行为,以及失败、取消和进程树完全停稳。
真实组装会覆盖未安装 Claude Code Bundle 与已安装该 Bundle 两种状态,并分别使用保留禁用行或授权工具的 Agent Preset。测试证明 Host 注册表和模型可见工具会反映这两个决策,组装期间不会启动产品进程,而且 Preset 编辑只影响后续 Session。现有 Codex Loader 与提供方测试会另行证明显式 Host 组装和宿主可执行文件解析。无密钥 ACPAgent Client Protocol)快照固定模型可见工具 schema,提供方测试则证明 Claude Code 的 SDK 平台载荷选择与无回退行为,以及失败、取消和进程树完全停稳。
## 考虑过的替代方案
@@ -34,6 +34,6 @@ Status: implemented
## 后果
用户只安装 Profile 可用的产品 Bundle,并通过与其他插件相同的 Agent Preset 创作路径管理模型可见授权。每个新会话会获得其 preset 工具行与 Profile 已安装提供方的交集。已安装但未授权的产品保持休眠,会产生包和模块加载开销,但不会启动产品进程、登录、调用模型或创建产品主目录;未安装的产品不会进入提供方或 SDK 依赖闭包。
用户只在需要 Claude Code 的 Profile 中安装该 Bundle;使用 Codex 的部署会显式挂载对应 Host 插件。模型可见授权仍通过与其他插件相同的 Agent Preset 创作路径管理。每个新 Session 会获得其 preset 工具行与 Host 可用提供方的交集。存在但未授权的产品保持休眠,会产生包和模块加载开销,但不会启动产品进程、登录、调用模型或创建产品主目录;缺席的产品不会进入提供方闭包。
Host 注册表仍是提供方的唯一权威,每个 Bundle 仍是部署可用性的权威,每个 Preset 仍是模型工具的权威。这个显式的双门生命周期避免全局启用开关,并让包移除与按会话创作保持独立。
Host 注册表仍是提供方的唯一权威,Profile Bundle 或显式 Host 组装仍是部署可用性的权威,每个 Preset 仍是模型工具的权威。这个显式的双门生命周期避免全局启用开关,并让包移除与按会话创作保持独立。
@@ -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: f9c7529db664d5b7bebcd77ba69fd974d6cad5b3
2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 8b0e42507c5a0850d569bff3c9abe962b4a03fe0
2026-08-04-claude-code-and-codex-subagent-backends.md: fed85f5d338db4b6e53406c94e3ecfd29aebd5af
2026-08-04-claude-code-and-codex-subagent-backends.zh.md: 2de5f72bc22bc56318209f13ca09746e6add58b2
@@ -12,7 +12,7 @@ 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) owns process-wide placement when a provider is installed, while the [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md) owns their optional direct Bundle installation and exclusion from the default distribution. 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 process-wide placement, while the [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md) owns both providers' exclusion from the default distribution and Claude Code's optional direct Bundle installation. Codex remains an explicitly mounted Host plugin. 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.
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.
@@ -65,7 +65,7 @@ The Codex evidence pins `@openai/codex@0.147.0` and `codex-cli 0.147.0`. Its rea
The Codex credentialed e2e registers the production provider, starts the same real app-server, and requests one random nonce through the test-private bridge described above. It fixes the external endpoint and model, stores no credential or request payload, requires exactly one completed upstream response, compares the trimmed product answer byte-for-byte with the nonce, and waits for every managed handle to exit.
The Claude Code evidence pins Agent SDK 0.3.220, Claude Code 2.1.220, and the identities and versions of all eight SDK platform packages. Its real-product spec lets the SDK select the installed payload, asserts that the shared subprocess argv begins with that package's native CLI, and observes the exact `x-api-key`, original task, byte-exact final answer, inherited temporary host-setting marker, process failure, local cancellation, and whole-tree exit. Unit coverage proves that production never resolves host `PATH`, omits the executable override, forwards the SDK-selected Windows `claude.exe` without a batch shim, and surfaces the SDK's missing-payload error without host fallback. This evidence proves the pinned official SDK/CLI integration rather than compatibility with independently installed Claude versions. Loader and optional Bundle-composition evidence resolve the selected product packages by name while starting neither product.
The Claude Code evidence pins Agent SDK 0.3.220, Claude Code 2.1.220, and the identities and versions of all eight SDK platform packages. Its real-product spec lets the SDK select the installed payload, asserts that the shared subprocess argv begins with that package's native CLI, and observes the exact `x-api-key`, original task, byte-exact final answer, inherited temporary host-setting marker, process failure, local cancellation, and whole-tree exit. Unit coverage proves that production never resolves host `PATH`, omits the executable override, forwards the SDK-selected Windows `claude.exe` without a batch shim, and surfaces the SDK's missing-payload error without host fallback. This evidence proves the pinned official SDK/CLI integration rather than compatibility with independently installed Claude versions. Loader coverage resolves Codex through explicit Host composition and Claude Code through its optional Bundle while starting neither product.
The Claude Code credentialed e2e maps the key and fixed official endpoint only in the provider's in-memory environment, uses the documented `deepseek-v4-pro[1m]` and `deepseek-v4-flash` model variables, and traverses the production provider, official SDK, and real CLI. It compares the trimmed result with a random nonce and proves whole-tree exit without calling the Messages API directly from the test.
@@ -87,7 +87,7 @@ The project owner's distribution authorization is scoped to the official `@anthr
## Consequences
Users delegate through two stable foreground tools backed by the official product integrations. Installed providers remain in the process-wide Host and tools remain per Preset under the [shared-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md); optional package availability and default exclusion are owned by the [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.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 foreground tools backed by the official product integrations. Available providers remain in the process-wide Host and tools remain per Preset under the [shared-host placement decision](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md); Claude Code Bundle availability and both providers' default exclusion are owned by the [production-closure decision](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.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. Codex behavior depends on the deployment's installed CLI and native configuration; Claude Code behavior depends on the Bundle-pinned platform CLI plus native account 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.
@@ -12,7 +12,7 @@ Status: implemented
## 决策
harness 交付两个同级的一次性提供方包:`codex``claude-code`。本说明负责它们的产品协议、结果映射和进程生命周期;[共享 profile 宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责提供方安装后的进程级放置,[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)负责其可选直接 Bundle 安装与默认发行排除。加载任一提供方都不会启动产品进程,而且每个工具只接受独立文本任务;产品选择与后台执行都不作为模型参数。
harness 交付两个同级的一次性提供方包:`codex``claude-code`。本说明负责它们的产品协议、结果映射和进程生命周期;[共享 profile 宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责进程级放置,[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)负责两个提供方的默认发行排除,以及 Claude Code 的可选直接 Bundle 安装。Codex 仍是显式挂载的 Host 插件。加载任一提供方都不会启动产品进程,而且每个工具只接受独立文本任务;产品选择与后台执行都不作为模型参数。
这两个提供方都报告 `inheritsParentContext: false`,不声明任何可选的启动能力,并传递父会话 cwd,但不会复制父级对话。文档所示的工具会禁用后台执行,并使用 `maxDepth: 'provider-managed'`,将递归策略留给进程外产品,而不是发送提供方无法强制执行的限制。每次调用都会创建一个全新的产品进程和一次不可续接的产品对话。共享 subagent 服务继续负责请求解析、生命周期事件、结果结算和前台收集;共享子进程服务负责凭证清洗、进程树终止以及整棵进程树的退出观测。
@@ -65,7 +65,7 @@ Codex 证据锁定 `@openai/codex@0.147.0` 与 `codex-cli 0.147.0`。其真实
带密钥 Codex e2e 会注册生产提供方,启动同样的真实 app-server,并通过上述测试专用桥接层请求一个随机数。该测试固定外部端点与模型,不存储任何凭据或请求载荷,要求上游恰好完成一次响应,将去除首尾空白后的产品答案与该随机数逐字节比较,并等待所有受管句柄退出。
Claude Code 证据会锁定 Agent SDK 0.3.220、Claude Code 2.1.220,以及八个 SDK 平台包的身份与版本。真实产品测试会让 SDK 选择已安装载荷,断言共享子进程 argv 以该包的原生 CLI 开头,并观测确切的 `x-api-key`、原始任务、逐字节完全一致的最终回答、继承的临时宿主设置标记、进程失败、本地取消和整棵进程树退出。单元覆盖会证明生产运行从不解析宿主 `PATH`、省略可执行文件覆盖、直接转发 SDK 所选的 Windows `claude.exe` 而不经过 batch shim,并且在载荷缺失时原样暴露 SDK 错误且不回退宿主 CLI。这项证据证明锁定的官方 SDK/CLI 集成,而不证明与独立安装的 Claude 版本兼容。Loader 与可选 Bundle 组装证据会按名称解析已选择的产品包且不启动产品。
Claude Code 证据会锁定 Agent SDK 0.3.220、Claude Code 2.1.220,以及八个 SDK 平台包的身份与版本。真实产品测试会让 SDK 选择已安装载荷,断言共享子进程 argv 以该包的原生 CLI 开头,并观测确切的 `x-api-key`、原始任务、逐字节完全一致的最终回答、继承的临时宿主设置标记、进程失败、本地取消和整棵进程树退出。单元覆盖会证明生产运行从不解析宿主 `PATH`、省略可执行文件覆盖、直接转发 SDK 所选的 Windows `claude.exe` 而不经过 batch shim,并且在载荷缺失时原样暴露 SDK 错误且不回退宿主 CLI。这项证据证明锁定的官方 SDK/CLI 集成,而不证明与独立安装的 Claude 版本兼容。Loader 覆盖会通过显式 Host 组装解析 Codex,并通过可选 Bundle 解析 Claude Code且不启动任一产品。
带密钥 Claude Code e2e 仅在提供方的内存环境中映射密钥与固定的官方端点,把模型变量设为文档所示的 `deepseek-v4-pro[1m]``deepseek-v4-flash`,并实际经过生产提供方、官方 SDK 与真实 CLI。它将去除首尾空白后的结果与一个随机数比较,并证明整棵进程树退出,且测试不会直接调用 Messages API。
@@ -87,7 +87,7 @@ Claude Code 证据会锁定 Agent SDK 0.3.220、Claude Code 2.1.220,以及八
## 后果
用户通过官方产品集成支持的两个稳定前台工具进行委派。已安装提供方位于进程级 Host、工具按 Preset 暴露,这些规则由[共享宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责;可选包可用性与默认排除由[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)负责。本说明规定的提供方生命周期会保留原生设置与行为,而共享服务继续独占任务结算与进程树完全停稳的责任。
用户通过官方产品集成支持的两个稳定前台工具进行委派。可用提供方位于进程级 Host、工具按 Preset 暴露,这些规则由[共享宿主归属决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)负责;Claude Code Bundle 可用性与两个提供方的默认排除由[生产依赖闭包决策](../simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md)负责。本说明规定的提供方生命周期会保留原生设置与行为,而共享服务继续独占任务结算与进程树完全停稳的责任。
每次委派都要承担新建产品进程和独立模型上下文的开销,且只有最终文本会到达父级。Codex 行为取决于部署环境中安装的 CLI 与原生配置;Claude Code 行为取决于 Bundle 锁定的平台 CLI,以及原生账户和工作区设置。带密钥 e2e 运行还会消耗外部 API 配额,并依赖 DeepSeek 官方端点;对协议、失败、取消与审批的确定性覆盖仍由无密钥层级承担。提供方不会恢复会话、以流式方式传送进度、接受新的人工交互、回滚工具或文件副作用,也不会施加按实际经过时间触发的超时。
@@ -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/simplification/2026-08-12-production-dsh-excludes-product-subagent-providers.md
2026-08-12-production-dsh-excludes-product-subagent-providers.md: b729115ead5ec6823b0c98815fa12f50022defdb
2026-08-12-production-dsh-excludes-product-subagent-providers.zh.md: ac10ae2b4f04ddc3997b5fe4bbb8f656742de547
2026-08-12-production-dsh-excludes-product-subagent-providers.md: 779bff6f668c086722f817a28a224182d0fbac09
2026-08-12-production-dsh-excludes-product-subagent-providers.zh.md: 27106668f8b81491d43e2033c1468799783c0584
@@ -10,13 +10,13 @@ English | [中文](2026-08-12-production-dsh-excludes-product-subagent-providers
## Decision
This decision partially supersedes only the default-inclusion part of the [shared-host placement](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md): `@deepseek-ai/dsh-base` does not depend on or mount the Codex and Claude Code subagent providers. Each existing provider package is instead a directly installable Profile Bundle whose `dsh.bundle.patch` points to one package-owned `cordis.patch.yml`. That patch contributes exactly one self-provider Host row and no Agent tool row.
This decision partially supersedes only the default-inclusion part of the [shared-host placement](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md): `@deepseek-ai/dsh-base` does not depend on or mount the Codex and Claude Code subagent providers. The Claude Code provider package is a directly installable Profile Bundle whose `dsh.bundle.patch` points to one package-owned `cordis.patch.yml`. That patch contributes exactly one self-provider Host row and no Agent tool row. The Codex package remains available for deployments that mount it explicitly.
The two Bundles remain independent. The Codex Bundle owns its `@deepseek-ai/dsh-sdk-protocol` runtime dependency and continues to use a host `codex` from `PATH`. The Claude Code Bundle owns the pinned Agent SDK and the matching platform CLI selected from the SDK's optional dependencies; production uses that private CLI and never falls back to a host `claude`. Installing one Bundle does not pull in the other, and the default `@deepseek-ai/dsh` production closure contains neither provider, the Claude Agent SDK, nor its platform payloads. An installed Bundle registers a dormant provider on the next Profile start, while an Agent Preset independently decides whether a new Session receives the corresponding tool. Installation brings only the selected package closure onto disk; it does not start a product, authenticate an account, rewrite native settings, or grant model access.
The two optional integrations remain independent. Codex continues to use a host `codex` from `PATH`. The Claude Code Bundle owns the pinned Agent SDK and the matching platform CLI selected from the SDK's optional dependencies; production uses that private CLI and never falls back to a host `claude`. Installing the Claude Code Bundle does not pull in the Codex package, and the default `@deepseek-ai/dsh` production closure contains neither provider, the Claude Agent SDK, nor its platform payloads. The Bundle registers a dormant provider on the next Profile start, while an Agent Preset independently decides whether a new Session receives its tool. Installation brings only the Claude Code package closure onto disk; it does not start a product, authenticate an account, rewrite native settings, or grant model access.
## Verification
Package tests pin each Bundle manifest, published patch, exact self-provider row, and product-specific runtime closure. Claude coverage pins Agent SDK 0.3.220, Claude Code 2.1.220, all eight platform package identities and versions, the SDK-selected executable entering the shared subprocess owner, and first-delegation failure without host fallback when the payload is missing. Workspace validation derives each published patch from its Bundle declaration rather than a package catalog. Production-closure tests prove the default, Codex-only, and Claude-only dependency boundaries, while real Bundle-patch and Agent-Preset composition covers all four installed sets, the full tool-grant matrix on a Host with both providers, representative missing-provider cases, and zero product processes. The base bundle test continues to reject both provider dependencies and configuration rows.
Package tests pin the Claude Code Bundle manifest, published patch, exact self-provider row, and runtime closure. Claude coverage pins Agent SDK 0.3.220, Claude Code 2.1.220, all eight platform package identities and versions, the SDK-selected executable entering the shared subprocess owner, and first-delegation failure without host fallback when the payload is missing. Workspace validation derives each published patch from its Bundle declaration rather than a package catalog. Production-closure tests prove the default and Claude-only dependency boundaries, while real Bundle-patch and Agent-Preset composition covers absent and installed Host states, disabled and enabled tool grants, later-Session adoption, and zero product processes. Existing Codex package tests continue to cover explicit Host composition and host executable resolution. The base bundle test continues to reject both provider dependencies and configuration rows.
## Alternatives considered
@@ -26,4 +26,4 @@ Package tests pin each Bundle manifest, published patch, exact self-provider row
## Consequences
Installing `@deepseek-ai/dsh` does not download either product provider through the base bundle. A Profile can add or remove either provider package, or both, directly; the changed Host availability takes effect on the next Profile start. Selecting Claude Code explicitly accepts its SDK and one large platform CLI payload, while selecting Codex does not install a product CLI. A separately authored Agent Preset still grants the model-visible tool only to newly composed Sessions. No wrapper package, meta Bundle, dynamic installer, or persisted product-enable state is introduced.
Installing `@deepseek-ai/dsh` does not download either product provider through the base bundle. A Profile can add or remove the Claude Code provider Bundle directly; the changed Host availability takes effect on the next Profile start and explicitly accepts its SDK plus one large platform CLI payload. A Codex deployment still mounts that provider explicitly and supplies its product CLI through `PATH`. A separately authored Agent Preset grants either model-visible tool only to newly composed Sessions. No wrapper package, meta Bundle, dynamic installer, or persisted product-enable state is introduced.
@@ -10,13 +10,13 @@ Status: implemented
## 决策
本决策只部分取代[共享 host 放置决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)中关于默认包含提供方的部分:`@deepseek-ai/dsh-base` 不依赖也不挂载 Codex 与 Claude Code subagent 提供方。现有的每个提供方包改为可直接安装的 Profile Bundle,其 `dsh.bundle.patch` 指向包自身拥有的 `cordis.patch.yml`。该 patch 恰好贡献一条挂载自身提供方的 Host 行,不包含 Agent 工具行。
本决策只部分取代[共享 host 放置决策](../architecture/2026-08-10-product-subagent-providers-in-shared-host.md)中关于默认包含提供方的部分:`@deepseek-ai/dsh-base` 不依赖也不挂载 Codex 与 Claude Code subagent 提供方。Claude Code 提供方包可直接安装的 Profile Bundle,其 `dsh.bundle.patch` 指向包自身拥有的 `cordis.patch.yml`。该 patch 恰好贡献一条挂载自身提供方的 Host 行,不包含 Agent 工具行。Codex 包仍供部署环境显式挂载。
两个 Bundle 彼此独立。Codex Bundle 自己负责运行时依赖 `@deepseek-ai/dsh-sdk-protocol`,并继续使用 `PATH` 中的宿主 `codex`。Claude Code Bundle 自己负责锁定的 Agent SDK,以及从 SDK optional dependencies 中选出的匹配平台 CLI;生产运行只使用该私有 CLI,绝不会回退到宿主 `claude`。安装其中一个 Bundle 不会带入另一个,默认的 `@deepseek-ai/dsh` 生产依赖闭包既不包含任一提供方,也不包含 Claude Agent SDK 或其平台载荷。已安装的 Bundle 会在下次 Profile 启动时注册一个休眠提供方,而 Agent Preset 独立决定新 Session 是否获得对应工具。安装只会把所选包闭包放到磁盘上;它不会启动产品、验证账户、改写原生设置或向模型授予访问权。
两个可选集成彼此独立。Codex 继续使用 `PATH` 中的宿主 `codex`。Claude Code Bundle 自己负责锁定的 Agent SDK,以及从 SDK optional dependencies 中选出的匹配平台 CLI;生产运行只使用该私有 CLI,绝不会回退到宿主 `claude`。安装 Claude Code Bundle 不会带入 Codex 包,默认的 `@deepseek-ai/dsh` 生产依赖闭包既不包含任一提供方,也不包含 Claude Agent SDK 或其平台载荷。 Bundle 会在下次 Profile 启动时注册一个休眠提供方,而 Agent Preset 独立决定新 Session 是否获得对应工具。安装只会把 Claude Code 包闭包放到磁盘上;它不会启动产品、验证账户、改写原生设置或向模型授予访问权。
## 验证
包测试会固定每个 Bundle 的 manifest、发布 patch、准确的自身提供方行以及产品专属运行时闭包。Claude 覆盖会固定 Agent SDK 0.3.220、Claude Code 2.1.220、八个平台包的身份与版本、SDK 所选可执行文件进入共享子进程责任方的路径,以及载荷缺失时第一次委派失败且不回退宿主 CLI。工作区验证会从 Bundle 声明派生每个发布 patch,而非维护包目录。生产闭包测试证明默认、仅 Codex 与仅 Claude 种依赖边界;真实 Bundle patch 与 Agent Preset 组装会覆盖四种安装集合、同时安装两个提供方时的完整工具授权矩阵、缺失提供方的代表场景以及零产品进程。base 组合包测试仍会拒绝这两个提供方依赖与配置行。
包测试会固定 Claude Code Bundle 的 manifest、发布 patch、准确的自身提供方行以及运行时闭包。Claude 覆盖会固定 Agent SDK 0.3.220、Claude Code 2.1.220、八个平台包的身份与版本、SDK 所选可执行文件进入共享子进程责任方的路径,以及载荷缺失时第一次委派失败且不回退宿主 CLI。工作区验证会从 Bundle 声明派生每个发布 patch,而非维护包目录。生产闭包测试证明默认与仅 Claude 种依赖边界;真实 Bundle patch 与 Agent Preset 组装会覆盖 Host 中缺席和已安装两种状态、禁用和启用两种工具授权、后续 Session 采纳以及零产品进程。现有 Codex 包测试继续覆盖显式 Host 组装和宿主可执行文件解析。base 组合包测试仍会拒绝这两个提供方依赖与配置行。
## 考虑过的替代方案
@@ -26,4 +26,4 @@ Status: implemented
## 后果
安装 `@deepseek-ai/dsh` 时,不会通过 base 组合包下载任一产品提供方。Profile 可以直接添加或移除任一提供方包,也可以同时操作两者;Host 可用性的变化会在下次 Profile 启动时生效。选择 Claude Code 代表明确接受其 SDK 与一个大型平台 CLI 载荷,而选择 Codex 不会安装产品 CLI。单独创作的 Agent Preset 仍只会向新组装的 Session 授予模型可见工具。本决策不引入 wrapper 包、meta Bundle、动态安装程序或持久化的产品启用状态。
安装 `@deepseek-ai/dsh` 时,不会通过 base 组合包下载任一产品提供方。Profile 可以直接添加或移除 Claude Code provider Bundle;Host 可用性的变化会在下次 Profile 启动时生效,并代表明确接受其 SDK 与一个大型平台 CLI 载荷Codex 部署仍须显式挂载该 provider,并通过 `PATH` 提供产品 CLI。单独创作的 Agent Preset 仍只会向新组装的 Session 授予任一模型可见工具。本决策不引入 wrapper 包、meta Bundle、动态安装程序或持久化的产品启用状态。
@@ -201,8 +201,8 @@
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Install the matching optional Provider Bundle in this Profile and restart
# the Host before enabling either template. Installation alone grants no tool.
# Install the optional Claude Code Provider Bundle in this Profile and restart
# the Host before enabling its template. Installation alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
@@ -188,8 +188,8 @@
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Install the matching optional Provider Bundle in this Profile and restart
# the Host before enabling either template. Installation alone grants no tool.
# Install the optional Claude Code Provider Bundle in this Profile and restart
# the Host before enabling its template. Installation alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
@@ -123,14 +123,14 @@ After a clean mount-validation, ask the user to start a session on the new prese
## Native product subagents
Codex and Claude Code providers are optional Profile Bundles. Install only the products the Profile needs, then restart the Profile so its Host registers those providers:
The Claude Code provider is an optional Profile Bundle. Install it only in Profiles that need it, then restart the Profile so its Host registers the provider:
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
The Bundle owns Host availability; the preset separately grants one Agent its ordinary delegation tool. Never move a product provider into the preset and never add a product-specific settings field. Removing either package with `dsh plugin --profile <name> remove <package>` withdraws only that provider on the next Profile start.
The Bundle owns Claude Code Host availability; the preset separately grants one Agent its ordinary delegation tool. Never move a product provider into the preset and never add a product-specific settings field. Removing the package withdraws the provider on the next Profile start.
Copy these disabled templates from a shipped full preset and remove `disabled` only for the products the user requested:
@@ -154,7 +154,7 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
maxDepth: provider-managed
```
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only an installed matching provider, and enabling both exposes the installed intersection. The host must provide `codex` on `PATH` for the Codex provider. The Claude Code Bundle installs and exclusively uses the matching platform CLI selected by its pinned Agent SDK; it does not inspect or fall back to a host `claude`, and a missing optional payload fails the first delegation. Neither Bundle nor the preset starts a product during composition, authenticates an account, selects a model, probes credentials, or manages native product settings.
The two rows are independent. The Claude Code row becomes available only when its Bundle is installed; the Codex row still requires a deployment whose Host composition registers that provider and whose `PATH` supplies `codex`. The Claude Code Bundle installs and exclusively uses the matching platform CLI selected by its pinned Agent SDK; it does not inspect or fall back to a host `claude`, and a missing optional payload fails the first delegation. Neither installing the Claude Code Bundle nor composing either preset row starts a product, authenticates an account, selects a model, probes credentials, or manages native product settings.
## What not to move into a preset
@@ -200,8 +200,8 @@
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Install the matching optional Provider Bundle in this Profile and restart
# the Host before enabling either template. Installation alone grants no tool.
# Install the optional Claude Code Provider Bundle in this Profile and restart
# the Host before enabling its template. Installation alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
+2 -2
View File
@@ -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 apps/cli/reference/README.md
README.md: f1fc9a2857fb143e435a9aa3cddbddfd03b72ee2
README.zh.md: 2d9036f80e602525405947beae8ee6c4cfcec645
README.md: c6c3f61c2910b84b9364ef7eaa13b8c0c011999b
README.zh.md: d6f3e7f1f508724e54e59df01e93199193ddc506
+2 -5
View File
@@ -42,17 +42,14 @@ dsh --profile web --patch ./extra.yml --dump-config
`dsh plugin --profile <name> <args...>` initializes the profile when missing (shipped template, or `@deepseek-ai/dsh-base` alone for other names), then forwards `<args...>` to `pnpm` with the profile directory as working directory — `add`, `remove`, `why`, `update`, and every other pnpm verb work unchanged; pnpm must be on PATH. Relative path specs (`.`, `../plugin`, and their `file:`/`link:` forms) are anchored to the invoking directory first, so `add .` from a plugin checkout installs that checkout, not the profile. After every successful run, `dsh.profile.bundles` is reconciled against the installed state: each dependency resolving to a package whose manifest declares `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` joins the layer stack (so an `update` that gains the declaration activates it), a bundle-less dependency stays plain with a one-time warning, and a removed dependency leaves the stack.
The Codex and Claude Code subagent providers are separate optional Bundles. Add either package, both in one command, or remove either package independently:
The Claude Code subagent provider is an optional Bundle. Add or remove it independently:
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
The successful pnpm operation changes the Profile manifest and Bundle list on disk; a running Profile keeps the Bundle set from its current start. Restart that Profile after adding, removing, or updating a Bundle. This startup boundary applies to Bundle membership, while ordinary edits to the Profile or home `cordis.patch.yml` take effect through hot reload. On the next start, each installed product Bundle registers only its dormant Host provider and starts no product process. The Codex provider resolves a host `codex` from `PATH`; the Claude Code Bundle instead installs the pinned Agent SDK and one matching private platform CLI, uses only that CLI, and never falls back to a host `claude`. Authentication and native product settings remain user-managed for both products; the [Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md) discloses the current platform payload size and missing-payload failure. Full Agent Presets keep both product tool rows disabled, so a copied Preset must separately enable the matching row before a new Agent can see that tool. Installing one provider never installs the other product package; the default dsh dependency closure includes neither provider nor the Claude Agent SDK or its platform payloads.
The successful pnpm operation changes the Profile manifest and Bundle list on disk; a running Profile keeps the Bundle set from its current start. Restart that Profile after adding, removing, or updating the Bundle. This startup boundary applies to Bundle membership, while ordinary edits to the Profile or home `cordis.patch.yml` take effect through hot reload. On the next start, the installed package registers only its dormant Host provider and starts no Claude process. The Bundle installs the pinned Agent SDK and one matching private platform CLI, uses only that CLI, and never falls back to a host `claude`. Authentication and native Claude settings remain user-managed; the [Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md) discloses the current platform payload size and missing-payload failure. Full Agent Presets keep both product tool rows disabled, so a copied Preset must separately enable the Claude Code row before a new Agent can see that tool. The Codex provider remains an explicitly mounted Host plugin that resolves `codex` from `PATH`; the default dsh dependency closure includes neither provider nor the Claude Agent SDK or its platform payloads.
```sh
dsh plugin --profile tui add github:deepseek-harness/turtle-ui
+2 -5
View File
@@ -42,17 +42,14 @@ dsh --profile web --patch ./extra.yml --dump-config
`dsh plugin --profile <name> <args...>` 在 profile 缺失时先初始化它(有随附模板的用模板,其他名称只装 `@deepseek-ai/dsh-base`),然后以 profile 目录为工作目录,把 `<args...>` 转发给 `pnpm``add``remove``why``update` 及其他所有 pnpm 子命令都照常可用;pnpm 必须在 PATH 上。相对路径 spec(`.``../plugin` 及其 `file:`/`link:` 形式)会先锚定到调用目录,因此在插件 checkout 中执行 `add .` 安装的是该 checkout,而不是 profile。每次成功运行后,系统都会根据当前安装状态更新 `dsh.profile.bundles`:如果某项依赖解析到的包在 manifest 中声明了 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`,该依赖就会加入配置层栈;如果某项依赖在 `update` 后获得该声明,也会随即激活。没有组合包声明的依赖仍作为普通依赖保留,并显示一次性警告;已移除的依赖则从配置层栈中删除。
Codex 与 Claude Code subagent provider 是两个彼此独立的可选 Bundle可以只添加一个包、在同一命令中添加两个包,或独立移除任一包
Claude Code subagent provider 是一个可选 Bundle可以独立添加或移除
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
pnpm 操作成功后只会改变磁盘上的 Profile manifest 与 Bundle 列表;正在运行的 Profile 会保留本次启动时的 Bundle 集合。添加、移除或更新 Bundle 后须重启该 Profile。这个启动边界只适用于 Bundle 成员变化,Profile 或 home 中普通 `cordis.patch.yml` 的编辑通过热重载生效。下一次启动时,每个已安装的产品 Bundle 只注册自己的休眠 Host provider,不会启动产品进程。Codex provider 会从 `PATH` 解析宿主 `codex`Claude Code Bundle 会安装锁定的 Agent SDK 与一个匹配的私有平台 CLI,只使用该 CLI,并且绝不会回退到宿主 `claude`两个产品的身份验证与原生设置仍由用户管理;[Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md)会披露当前平台载荷体积与载荷缺失时的失败行为。完整 Agent Preset 中的两个产品工具行仍默认禁用,因此还须在复制出的 Preset 中单独启用对应行,新 Agent 才能看到该工具。只安装一个 provider 不会安装另一个产品包;默认 dsh 依赖闭包不包含任一 provider,也不包含 Claude Agent SDK 或其平台载荷。
pnpm 操作成功后只会改变磁盘上的 Profile manifest 与 Bundle 列表;正在运行的 Profile 会保留本次启动时的 Bundle 集合。添加、移除或更新 Bundle 后须重启该 Profile。这个启动边界只适用于 Bundle 成员变化,Profile 或 home 中普通 `cordis.patch.yml` 的编辑通过热重载生效。下一次启动时,已安装的包只注册休眠 Host provider,不会启动 Claude 进程。该 Bundle 会安装锁定的 Agent SDK 与一个匹配的私有平台 CLI,只使用该 CLI,并且绝不会回退到宿主 `claude`Claude 的身份验证与原生设置仍由用户管理;[Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md)会披露当前平台载荷体积与载荷缺失时的失败行为。完整 Agent Preset 中的两个产品工具行仍默认禁用,因此还须在复制出的 Preset 中单独启用 Claude Code 行,新 Agent 才能看到该工具。Codex provider 仍须作为 Host 插件显式挂载,并从 `PATH` 解析 `codex`;默认 dsh 依赖闭包不包含任一 provider,也不包含 Claude Agent SDK 或其平台载荷。
```sh
dsh plugin --profile tui add github:deepseek-harness/turtle-ui
+18 -39
View File
@@ -26,7 +26,6 @@ const REPO_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
/** The shipped Web surface: the dsh-base and dsh-web-app bundle patches over an empty preset root. */
const BASE_PATCH = join(REPO_ROOT, 'packages/bundle/base/cordis.patch.yml')
const WEB_PATCH = join(REPO_ROOT, 'packages/bundle/web-app/cordis.patch.yml')
const CODEX_PATCH = join(REPO_ROOT, 'packages/subagent/subagent-codex/cordis.patch.yml')
const CLAUDE_CODE_PATCH = join(REPO_ROOT, 'packages/subagent/subagent-claude-code/cordis.patch.yml')
/** The installation anchor whose dependency surface the preset module fallback mirrors. */
const INSTALL_ANCHOR = join(REPO_ROOT, 'apps/cli/package.json')
@@ -434,12 +433,11 @@ describe('the shipped Web composition', () => {
})
})
describe('product subagent Bundle and user-preset intersection', () => {
const presetIds = ['products-none', 'products-codex', 'products-claude', 'products-both'] as const
type Product = 'codex' | 'claude-code'
describe('Claude Code Bundle and user-preset intersection', () => {
const presetIds = ['products-none', 'products-claude'] as const
type PresetId = typeof presetIds[number]
async function bootProducts(installed: readonly Product[]): Promise<Context> {
async function bootProducts(installed: boolean): Promise<Context> {
const root = await mkdtemp(join(tmpdir(), 'dsh-product-presets-'))
const userRoot = join(root, 'presets')
const settingsFile = join(root, 'settings.yaml')
@@ -447,20 +445,14 @@ describe('product subagent Bundle and user-preset intersection', () => {
await writeFile(settingsFile, '{}\n')
for (const id of presetIds) {
let composition = standard
if (id === 'products-codex' || id === 'products-both') {
composition = enablePresetTool(composition, 'tool-subagent-codex')
}
if (id === 'products-claude' || id === 'products-both') {
if (id === 'products-claude') {
composition = enablePresetTool(composition, 'tool-subagent-claude-code')
}
const directory = join(userRoot, id)
await mkdir(directory, { recursive: true })
await writeFile(join(directory, 'agent.cordis.yml'), composition)
}
const productPatches = installed.flatMap(product => loadOverlayPatches(
'dsh-test',
product === 'codex' ? CODEX_PATCH : CLAUDE_CODE_PATCH,
))
const productPatches = installed ? loadOverlayPatches('dsh-test', CLAUDE_CODE_PATCH) : []
return await bootWeb(settingsFile, [
...productPatches,
{
@@ -474,21 +466,13 @@ describe('product subagent Bundle and user-preset intersection', () => {
includeUserRoot: false,
},
},
], installed.map(product => dirname(product === 'codex' ? CODEX_PATCH : CLAUDE_CODE_PATCH)))
], installed ? [dirname(CLAUDE_CODE_PATCH)] : [])
}
it('composes the intersection of installed Bundles and enabled preset rows', async () => {
const enabledByPreset: Record<PresetId, Product[]> = {
'products-none': [],
'products-codex': ['codex'],
'products-claude': ['claude-code'],
'products-both': ['codex', 'claude-code'],
}
const scenarios: Array<{ installed: Product[]; presets: readonly PresetId[] }> = [
{ installed: [], presets: ['products-both'] },
{ installed: ['codex'], presets: ['products-both'] },
{ installed: ['claude-code'], presets: ['products-both'] },
{ installed: ['codex', 'claude-code'], presets: presetIds },
it('composes the intersection of the installed Bundle and enabled preset row', async () => {
const scenarios: Array<{ installed: boolean; presets: readonly PresetId[] }> = [
{ installed: false, presets: presetIds },
{ installed: true, presets: presetIds },
]
for (const { installed, presets } of scenarios) {
@@ -498,21 +482,16 @@ describe('product subagent Bundle and user-preset intersection', () => {
expect(productCtx.subagents.list()
.filter(name => name === 'codex' || name === 'claude-code')
.sort())
.toEqual([...installed].sort())
.toEqual(installed ? ['claude-code'] : [])
for (const id of presets) {
const enabled = enabledByPreset[id]
const handle = await productCtx.agents.create({
sessionId: SessionId(`preset-${id}-${installed.join('-') || 'none'}-${randomUUID()}`),
sessionId: SessionId(`preset-${id}-${installed ? 'claude' : 'none'}-${randomUUID()}`),
setup: agentCtx => productCtx.agentPresets.mount(agentCtx, id).then(() => undefined),
})
try {
const expectedTools = enabled
.filter(product => installed.includes(product))
.map(product => product === 'codex' ? 'subagent_codex' : 'subagent_claude_code')
.sort()
expect(toolNames(productCtx, handle.agent)
.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
.toEqual(expectedTools)
.toEqual(installed && id === 'products-claude' ? ['subagent_claude_code'] : [])
} finally {
await handle.dispose()
}
@@ -526,7 +505,7 @@ describe('product subagent Bundle and user-preset intersection', () => {
}, 120_000)
it('applies a product-row edit only to later sessions on the preset', async () => {
const productCtx = await bootProducts(['codex'])
const productCtx = await bootProducts(true)
const preset = await productCtx.agentPresets.resolve('products-none')
const original = await readFile(preset.path, 'utf8')
const existing = await productCtx.agents.create({
@@ -534,16 +513,16 @@ describe('product subagent Bundle and user-preset intersection', () => {
setup: agentCtx => productCtx.agentPresets.mount(agentCtx, 'products-none').then(() => undefined),
})
try {
expect(toolNames(productCtx, existing.agent)).not.toContain('subagent_codex')
await writeFile(preset.path, enablePresetTool(original, 'tool-subagent-codex'))
expect(toolNames(productCtx, existing.agent)).not.toContain('subagent_claude_code')
await writeFile(preset.path, enablePresetTool(original, 'tool-subagent-claude-code'))
const later = await productCtx.agents.create({
sessionId: SessionId('preset-product-generation-later'),
setup: agentCtx => productCtx.agentPresets.mount(agentCtx, 'products-none').then(() => undefined),
})
try {
expect(toolNames(productCtx, existing.agent)).not.toContain('subagent_codex')
expect(toolNames(productCtx, later.agent)).toContain('subagent_codex')
expect(toolNames(productCtx, existing.agent)).not.toContain('subagent_claude_code')
expect(toolNames(productCtx, later.agent)).toContain('subagent_claude_code')
} finally {
await later.dispose()
}
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/module-graph.md
module-graph.md: 46f924b0df80aa667d4929e6d2707c8cf051bfb2
module-graph.zh.md: 3a226bbdacfd1a5b850a406bea66bf29a5db209b
module-graph.md: 24ee9a4815b4236336ae37f6c926dba4718dafb9
module-graph.zh.md: d87feb946a43a2f9d391e7894b4a143af4e18406
+8 -7
View File
@@ -961,12 +961,6 @@ flowchart TD
pkg_subagent_claude_code --> pkg_subagent
pkg_subagent_claude_code --> pkg_subprocess
pkg_subagent_claude_code --> pkg_timeout
pkg_subagent_codex --> pkg_invariants
pkg_subagent_codex --> pkg_llm
pkg_subagent_codex --> pkg_session
pkg_subagent_codex --> pkg_subagent
pkg_subagent_codex --> pkg_subprocess
pkg_subagent_codex --> pkg_timeout
pkg_subagent_in_process_driver --> pkg_agent
pkg_subagent_in_process_driver --> pkg_invariants
pkg_subagent_in_process_driver --> pkg_llm
@@ -1074,6 +1068,13 @@ flowchart TD
pkg_workflow_worker_thread --> pkg_subagent
pkg_workflow_worker_thread --> pkg_tools
pkg_workflow_worker_thread --> pkg_workflow
pkg_subagent_codex --> pkg_invariants
pkg_subagent_codex --> pkg_llm
pkg_subagent_codex --> pkg_sdk_protocol
pkg_subagent_codex --> pkg_session
pkg_subagent_codex --> pkg_subagent
pkg_subagent_codex --> pkg_subprocess
pkg_subagent_codex --> pkg_timeout
pkg_subagent_fork_in_process --> pkg_agent
pkg_subagent_fork_in_process --> pkg_invariants
pkg_subagent_fork_in_process --> pkg_session
@@ -1576,7 +1577,6 @@ flowchart TD
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-codex`](../packages/subagent/subagent-codex) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
@@ -1592,6 +1592,7 @@ flowchart TD
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-codex`](../packages/subagent/subagent-codex) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`client-runtime`](../packages/client/runtime) | `client` | [`api-remotes`](../packages/api/remotes), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry) |
+8 -7
View File
@@ -963,12 +963,6 @@ flowchart TD
pkg_subagent_claude_code --> pkg_subagent
pkg_subagent_claude_code --> pkg_subprocess
pkg_subagent_claude_code --> pkg_timeout
pkg_subagent_codex --> pkg_invariants
pkg_subagent_codex --> pkg_llm
pkg_subagent_codex --> pkg_session
pkg_subagent_codex --> pkg_subagent
pkg_subagent_codex --> pkg_subprocess
pkg_subagent_codex --> pkg_timeout
pkg_subagent_in_process_driver --> pkg_agent
pkg_subagent_in_process_driver --> pkg_invariants
pkg_subagent_in_process_driver --> pkg_llm
@@ -1076,6 +1070,13 @@ flowchart TD
pkg_workflow_worker_thread --> pkg_subagent
pkg_workflow_worker_thread --> pkg_tools
pkg_workflow_worker_thread --> pkg_workflow
pkg_subagent_codex --> pkg_invariants
pkg_subagent_codex --> pkg_llm
pkg_subagent_codex --> pkg_sdk_protocol
pkg_subagent_codex --> pkg_session
pkg_subagent_codex --> pkg_subagent
pkg_subagent_codex --> pkg_subprocess
pkg_subagent_codex --> pkg_timeout
pkg_subagent_fork_in_process --> pkg_agent
pkg_subagent_fork_in_process --> pkg_invariants
pkg_subagent_fork_in_process --> pkg_session
@@ -1578,7 +1579,6 @@ flowchart TD
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-codex`](../packages/subagent/subagent-codex) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent-control`](../packages/subagent/tool-subagent-control) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
@@ -1594,6 +1594,7 @@ flowchart TD
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-codex`](../packages/subagent/subagent-codex) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`client-runtime`](../packages/client/runtime) | `client` | [`api-remotes`](../packages/api/remotes), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry) |
@@ -1,5 +1,5 @@
# Test-only composition of the foreground tool around a Bundle-supplied provider.
# The owning e2e applies the package's real patch and never invokes the model or Codex.
# Test-only composition of the public opt-in provider and foreground tool.
# The owning e2e boots this tree but never invokes the model or Codex.
- id: fixture
name: './fixture.ts'
@@ -9,6 +9,9 @@
- id: subprocess
name: '@deepseek-ai/dsh-subprocess-local'
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
@@ -1,21 +1,20 @@
#!/usr/bin/env node
/** Inspect the public Codex provider composition without invoking the product. */
import { boot, loadOverlayPatches, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import type {} from '@deepseek-ai/dsh-subagent'
import type {} from '@deepseek-ai/dsh-tools'
const configPath = process.argv[2]
const bundlePatchPath = process.argv[3]
if (configPath === undefined || bundlePatchPath === undefined) {
throw new Error('subagent-codex Loader composition driver requires config and Bundle patch paths')
if (configPath === undefined) {
throw new Error('subagent-codex Loader composition driver requires a config path')
}
let starts = 0
const ctx = await boot(
'subagent-codex-loader-composition',
resolveConfigPath(configPath, undefined),
loadOverlayPatches('subagent-codex-loader-composition', bundlePatchPath),
undefined,
(hostCtx) => {
hostCtx.on('subagent/start', () => {
starts += 1
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -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/bundle/README.md
README.md: 4d7a064939ae04f25737b324ec35332b7b944f80
README.zh.md: 8910b33a97acd2ef3ee5b659305739246004de01
README.md: 3afa53c1444b43c38b7a71f3e9e00d271078be54
README.zh.md: 740b3579ce1555f2b1b26ca79e8a3d915a338193
+1 -1
View File
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
Profile bundles: npm packages whose manifest declares `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`, making them installable patch layers for `dsh --profile` compositions ([profile contract](../boot/app-boot/README.md#profiles)). A bundle's substance is its patch list; some also ship runtime glue plugins their patch mounts.
The manifest declaration, not this directory, defines Bundle identity. Domain packages can carry their own optional Profile layer; the [Codex and Claude Code subagent packages](../subagent/README.md) are directly installable examples.
The manifest declaration, not this directory, defines Bundle identity. Domain packages can carry their own optional Profile layer; the [Claude Code subagent package](../subagent/subagent-claude-code/README.md) is a directly installable example.
| Package | Role | ctx key |
|---|---|---|
+1 -1
View File
@@ -4,7 +4,7 @@
Profile 组合包:在 manifest(元数据清单)中声明 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` 的 npm 包,因此可作为 patch 层安装进 `dsh --profile` 组合([profile 约定](../boot/app-boot/README.md#profiles))。组合包的实体是它的 patch 列表;有些组合包还附带由其 patch 挂载的运行时粘合插件。
Bundle 身份由 manifest 声明决定,而不是由本目录决定。领域包可以携带自己的可选 Profile 层;[Codex 与 Claude Code subagent 包](../subagent/README.md)就是可直接安装的例子。
Bundle 身份由 manifest 声明决定,而不是由本目录决定。领域包可以携带自己的可选 Profile 层;[Claude Code subagent 包](../subagent/subagent-claude-code/README.md)就是可直接安装的例子。
| 包 | 职责 | ctx key |
|---|---|---|
+2 -2
View File
@@ -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/bundle/base/README.md
README.md: a963bcca671c613ebdcc7b453384b1d9b8393662
README.zh.md: ab43954fe3f4ad46160961e8ca67876df9aac503
README.md: 5fdd642ecc03fea77b2b00fc6428525c1a40d891
README.zh.md: c2a07ec15816e41eadf682bcd8631c93bce77ae0
+1 -1
View File
@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
The shared dsh core as a profile bundle: [`cordis.patch.yml`](cordis.patch.yml) inserts every base plugin row — model adapters, the shared [`agent-default-model`](../../core/agent-default-model/README.md) selection, tools, persistence, policy, settings/credentials, telemetry, and the core spawn/fork subagent providers — over the empty profile root, as the first layer of every profile's `dsh.profile.bundles` list. The optional Codex and Claude Code providers stay outside this package and its production dependency closure; a Profile installs either [product provider package](../../subagent/README.md) only when needed. The default `@deepseek-ai/dsh` production closure therefore includes neither product provider nor the Claude Agent SDK. Later bundle layers (e.g. [`dsh-web-app`](../web-app/README.md)) and the user's profile `cordis.patch.yml` override these rows by id; a patch replaces a row's whole `config`, so mode-specific values live in mode bundles, not here. The package has no runtime API; the profile composer resolves the patch through the `dsh.bundle.patch` manifest field, never through code.
The shared dsh core as a profile bundle: [`cordis.patch.yml`](cordis.patch.yml) inserts every base plugin row — model adapters, the shared [`agent-default-model`](../../core/agent-default-model/README.md) selection, tools, persistence, policy, settings/credentials, telemetry, and the core spawn/fork subagent providers — over the empty profile root, as the first layer of every profile's `dsh.profile.bundles` list. The optional Codex and Claude Code providers stay outside this package and its production dependency closure; a Profile can install the [Claude Code provider Bundle](../../subagent/subagent-claude-code/README.md) only when needed, while a deployment that uses Codex still mounts that provider explicitly. The default `@deepseek-ai/dsh` production closure therefore includes neither product provider nor the Claude Agent SDK. Later bundle layers (e.g. [`dsh-web-app`](../web-app/README.md)) and the user's profile `cordis.patch.yml` override these rows by id; a patch replaces a row's whole `config`, so mode-specific values live in mode bundles, not here. The package has no runtime API; the profile composer resolves the patch through the `dsh.bundle.patch` manifest field, never through code.
The patch gates both shell stacks by platform on its own rows: `bash-sandbox`/`tool-bash` carry `disabled: !!js process.platform === 'win32'` (bash has no Windows runner), and their twins `pwsh-sandbox`/`tool-pwsh` mount on win32 only with the inverted expression — one shared patch file, exactly one shell stack per host. The permission surface stays exactly as on POSIX: `sandbox`/`sandbox-policy` enforce the file-effect policy through the Windows ACL restricted-token runner (the win32 chain of `dsh-sandbox-local``@deepseek-ai/dsh-sandbox-windows-acl`), the permission switcher and the approval service run unchanged, and `fs-sandbox` keeps fencing `ctx.fs` writes — mounting `dsh-fs-local` alongside it would double-register `ctx.fs` and fail the load. A Windows host that prefers the unconfined local pwsh executor or full access overrides these rows through its profile or home `cordis.patch.yml` (the bash-restore recipe must be complete: disable `pwsh-sandbox`/`tool-pwsh` AND re-enable `bash-sandbox`/`tool-bash` — both executor families register the same `bash` service, so an incomplete recipe fails loud at load). POSIX hosts see the pwsh rows disabled.
+2 -2
View File
@@ -2,7 +2,7 @@
[English](README.md) | 中文
以 profile 组合包形式交付的共享 dsh 核心:[`cordis.patch.yml`](cordis.patch.yml) 在空的 profile 根之上插入全部基础插件行——模型适配器、共享的 [`agent-default-model`](../../core/agent-default-model/README.md) 选择、工具、持久化、策略、settingscredentials、遥测与核心 spawnfork subagent provider——作为每个 profile 的 `dsh.profile.bundles` 列表中的第一层。可选的 Codex 与 Claude Code provider 不属于本包及其生产依赖闭包;Profile 仅在需要时安装对应的[产品 provider ](../../subagent/README.md)。因此,默认的 `@deepseek-ai/dsh` 生产依赖闭包既不包含任一产品 provider,也不包含 Claude Agent SDK。后续的组合包层(例如 [`dsh-web-app`](../web-app/README.md))和用户 profile 的 `cordis.patch.yml` 按 id 覆盖这些行;patch 会替换目标行的整个 `config`,因此模式专属的值放在各模式组合包中,而不是这里。该包没有运行时 API;profile 组合器通过 manifest(元数据清单)的 `dsh.bundle.patch` 字段解析 patch,绝不通过代码。
以 profile 组合包形式交付的共享 dsh 核心:[`cordis.patch.yml`](cordis.patch.yml) 在空的 profile 根之上插入全部基础插件行——模型适配器、共享的 [`agent-default-model`](../../core/agent-default-model/README.md) 选择、工具、持久化、策略、settingscredentials、遥测与核心 spawnfork subagent provider——作为每个 profile 的 `dsh.profile.bundles` 列表中的第一层。可选的 Codex 与 Claude Code provider 不属于本包及其生产依赖闭包;Profile 可以仅在需要时安装 [Claude Code provider Bundle](../../subagent/subagent-claude-code/README.md),使用 Codex 的部署仍须显式挂载该 provider。因此,默认的 `@deepseek-ai/dsh` 生产依赖闭包既不包含任一产品 provider,也不包含 Claude Agent SDK。后续的组合包层(例如 [`dsh-web-app`](../web-app/README.md))和用户 profile 的 `cordis.patch.yml` 按 id 覆盖这些行;patch 会替换目标行的整个 `config`,因此模式专属的值放在各模式组合包中,而不是这里。该包没有运行时 API;profile 组合器通过 manifest(元数据清单)的 `dsh.bundle.patch` 字段解析 patch,绝不通过代码。
patch 在自身上按平台门控两个 shell 栈:`bash-sandbox`/`tool-bash` 携带 `disabled: !!js process.platform === 'win32'`bash 没有 Windows runner),它们的孪生行 `pwsh-sandbox`/`tool-pwsh` 以取反的表达式仅在 win32 挂载——同一份 patch 文件,每个宿主恰好挂载一个 shell 栈。权限面与 POSIX 完全一致:`sandbox`/`sandbox-policy` 通过 Windows ACL 受限令牌 runner`dsh-sandbox-local` 的 win32 链 → `@deepseek-ai/dsh-sandbox-windows-acl`)执行文件效果策略,权限切换器与 approval 服务原样运行,`fs-sandbox` 继续围栏 `ctx.fs` 写入——在其旁再挂载 `dsh-fs-local` 会重复注册 `ctx.fs` 并在加载时失败。偏好不受沙盒约束的本地 pwsh 执行器或完整访问的 Windows 主机通过其 profile 或 home 的 `cordis.patch.yml` 覆盖这些行(bash 恢复配方必须完整:禁用 `pwsh-sandbox`/`tool-pwsh` 并重新启用 `bash-sandbox`/`tool-bash`——两个执行器家族注册同一个 `bash` 服务,配方不完整会在加载时直接报错)。POSIX 主机看到的是被禁用的 pwsh 行。
@@ -19,4 +19,4 @@ patch 在自身上按平台门控两个 shell 栈:`bash-sandbox`/`tool-bash`
## 已知限制与暂缓事项
- **patch 会替换整行 `config`**:profile 覆盖必须重述该行需要保留的每个字段;不存在深度合并层。
- **Windows 的临时目录授权是按会话的私有子目录**——`workspace-write` 把写入限制在工作区与会话自己的 temp 子目录(`<temp>\dsh-<hash>`,受限子进程的 TMP/TEMP 被改写);`read-only` 不授予任何写入。见 `@deepseek-ai/dsh-sandbox-windows-acl`
- **Windows 的临时目录授权是按会话的私有子目录**——`workspace-write` 把写入限制在工作区与会话自己的 temp 子目录(`<temp>\dsh-<hash>`,受限子进程的 TMP/TEMP 被改写);`read-only` 不授予任何临时目录写入权限。见 `@deepseek-ai/dsh-sandbox-windows-acl`
+2 -2
View File
@@ -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/README.md
README.md: 997d05c5030cc4e1ee9ebc1e8b4da2e6056b6266
README.zh.md: a0e3ff4e17c1e0e092d56dc518bb16f0329c0198
README.md: aebf368b984dca3ac2e37d1bcdba26a82ce85196
README.zh.md: fb9e223f916a8b07d3a4ae254fa61087de081a93
+1 -1
View File
@@ -18,7 +18,7 @@ This family lets an agent delegate work to child agents. Multiple named provider
| [`tool-subagent-control/`](tool-subagent-control/README.md) | Exposes child messaging and listing to the model | registers on `ctx.tools` |
| [`tool-subagent-report/`](tool-subagent-report/README.md) | Provides the child-to-parent report channel | registers in child scopes |
The Codex and Claude Code packages are also independent Profile Bundles. Install either or both with `dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code`, then restart that Profile; each installed package registers only its own dormant Host provider. Full Agent Presets keep separate disabled tool templates, so installation alone exposes no model tool. Removing one package withdraws only that provider on the next Profile start.
The Claude Code package is also an optional Profile Bundle. Install it with `dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code`, then restart that Profile; the package registers only its dormant Host provider, while a copied Agent Preset separately grants the disabled tool template to new Sessions. Removing the package withdraws that provider on the next Profile start. The Codex package remains an explicitly mounted Host plugin and uses a host `codex` from `PATH`.
See the decisions for the [capability family](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [continuable children](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), and [control tools](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md).
+1 -1
View File
@@ -18,7 +18,7 @@
| [`tool-subagent-control/`](tool-subagent-control/README.md) | 向模型公开子级消息发送和列举操作 | 注册到 `ctx.tools` |
| [`tool-subagent-report/`](tool-subagent-report/README.md) | 提供从子级到父级的报告通道 | 注册到子级作用域 |
Codex 与 Claude Code 包也分别是独立的 Profile Bundle。使用 `dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code` 安装其中一个或两个包,再重启该 Profile每个已安装包只注册自己的休眠 Host provider。完整 Agent Preset 仍保留彼此独立且默认禁用的工具模板,因此只安装 Bundle 不会向模型暴露工具。移除其中一个包后,下一次 Profile 启动会撤回对应 provider。
Claude Code 包也是一个可选的 Profile Bundle。使用 `dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code` 安装重启该 Profile包只注册休眠 Host provider,而复制出的 Agent Preset 会单独把默认禁用的工具模板授予新 Session。移除该包后,下一次 Profile 启动会撤回对应 provider。Codex 包仍须作为 Host 插件显式挂载,并使用 `PATH` 中的宿主 `codex`
参见有关[能力家族](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[可继续执行的子级](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)和[控制工具](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)的决策。
@@ -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: 058e72a46cd6fb7c15779f3650e191535a8b2571
README.zh.md: 05914165c26162de9c0a37cfa7090a8d667e63bc
README.md: 226270506d743a7bb6023e27ae52c7e32b49c848
README.zh.md: 453d1f15004267137f58969820e2de8d08ae943a
@@ -63,7 +63,7 @@ Installation controls Host availability, not model permission. Full Agent Preset
## Product compatibility and evidence
The runtime dependency is pinned to `@anthropic-ai/claude-agent-sdk@0.3.220`, whose eight platform packages carry Claude Code 2.1.220. A normal install selects one payload for the current OS, CPU, and Linux libc. For the current darwin-arm64 payload, `npm pack --dry-run --json` reports 74,858,812 packed bytes and 256,908,856 unpacked bytes; other platforms may differ, and these values are disclosure rather than an installation threshold. The keyless real-product test runs the SDK-selected CLI against a loopback Messages fixture and asserts that the shared subprocess argv begins with that platform package's native executable. Loader composition proves that both product packages coexist without starting either product.
The runtime dependency is pinned to `@anthropic-ai/claude-agent-sdk@0.3.220`, whose eight platform packages carry Claude Code 2.1.220. A normal install selects one payload for the current OS, CPU, and Linux libc. For the current darwin-arm64 payload, `npm pack --dry-run --json` reports 74,858,812 packed bytes and 256,908,856 unpacked bytes; other platforms may differ, and these values are disclosure rather than an installation threshold. The keyless real-product test runs the SDK-selected CLI against a loopback Messages fixture and asserts that the shared subprocess argv begins with that platform package's native executable. Loader composition proves that installing the Bundle registers only the dormant Claude Code provider and starts no product process.
Installing with optional dependencies omitted, using an unsupported platform, or losing the selected payload leaves provider registration dormant but makes the first delegation fail with the SDK's native-payload startup error. The provider neither probes a host CLI nor retries with one.
@@ -63,7 +63,7 @@ dsh --profile <name>
## 产品兼容性与证据
运行时依赖精确锁定为 `@anthropic-ai/claude-agent-sdk@0.3.220`,其八个平台包都携带 Claude Code 2.1.220。普通安装会按当前操作系统、CPU 及 Linux libc 选择一个载荷。对于当前 darwin-arm64 载荷,`npm pack --dry-run --json` 报告压缩包为 74,858,812 字节、解包后为 256,908,856 字节;其他平台可能不同,这些数值只用于披露而不是安装阈值。无密钥真实产品测试会让 SDK 选择 CLI,通过回环 Messages fixture 运行它,并断言共享子进程 argv 的首项就是该平台包的原生可执行文件。Loader 组合证明两个产品包能够共存且不会启动任一产品
运行时依赖精确锁定为 `@anthropic-ai/claude-agent-sdk@0.3.220`,其八个平台包都携带 Claude Code 2.1.220。普通安装会按当前操作系统、CPU 及 Linux libc 选择一个载荷。对于当前 darwin-arm64 载荷,`npm pack --dry-run --json` 报告压缩包为 74,858,812 字节、解包后为 256,908,856 字节;其他平台可能不同,这些数值只用于披露而不是安装阈值。无密钥真实产品测试会让 SDK 选择 CLI,通过回环 Messages fixture 运行它,并断言共享子进程 argv 的首项就是该平台包的原生可执行文件。Loader 组合证明安装该 Bundle 只会注册休眠的 Claude Code provider,不会启动产品进程
如果安装时省略 optional dependencies、当前平台不受支持,或所选载荷缺失,提供方注册仍保持休眠,但第一次委派会以 SDK 的原生载荷启动错误失败。提供方既不会探测宿主 CLI,也不会用它重试。
@@ -257,15 +257,25 @@ export async function startClaudeCodeRun(
spawnError = thrown(childError)
}
if (cancelledBeforeCleanup || isAborted(request.signal)) {
throw new Error('subagent-claude-code: request was aborted before SDK startup')
}
const cancelled = cancelledBeforeCleanup || isAborted(request.signal)
if (closeError !== undefined) {
const failures = cancelled
? [
new Error('subagent-claude-code: request was aborted before SDK startup'),
spawnError,
closeError,
]
: [spawnError, closeError]
throw new AggregateError(
[spawnError, closeError],
`subagent-claude-code: Claude Code process startup failed: ${spawnError.message}; query cleanup also failed`,
failures,
cancelled
? `subagent-claude-code: request was aborted before SDK startup; Claude Code process startup also failed: ${spawnError.message}; query cleanup also failed`
: `subagent-claude-code: Claude Code process startup failed: ${spawnError.message}; query cleanup also failed`,
)
}
if (cancelled) {
throw new Error('subagent-claude-code: request was aborted before SDK startup')
}
throw spawnError
}
if (child !== undefined) {
@@ -945,6 +945,34 @@ describe('run publication, cancellation, and settlement', () => {
)).rejects.toThrow('aborted before SDK startup')
expect(cancelledFailedClose).toHaveBeenCalledOnce()
const cancelledFailedSpawnCloseError = new Error('cancelled query close failed')
const cancelledFailedSpawnClose = vi.fn(() => {
throw cancelledFailedSpawnCloseError
})
const cancelledFailedSpawnWithCloseFailure = fakeChild({
pid: -1,
doneError: spawnError,
})
const failedSpawnAbortWithCloseFailure = new AbortController()
queryMock.mockImplementationOnce(({ options }) => {
options.spawnClaudeCodeProcess!(sdkSpawnOptions())
failedSpawnAbortWithCloseFailure.abort(new Error('startup cancelled'))
return queryFrom([], undefined, cancelledFailedSpawnClose)
})
const cancelledWithCloseFailure = startClaudeCodeRun(
request(undefined, failedSpawnAbortWithCloseFailure.signal),
{ ...unused.spec, spawn: () => cancelledFailedSpawnWithCloseFailure.handle },
)
await expect(cancelledWithCloseFailure).rejects.toMatchObject({
message: 'subagent-claude-code: request was aborted before SDK startup; Claude Code process startup also failed: spawn /sdk/claude EACCES; query cleanup also failed',
errors: [
expect.objectContaining({ message: 'subagent-claude-code: request was aborted before SDK startup' }),
spawnError,
cancelledFailedSpawnCloseError,
],
})
expect(cancelledFailedSpawnClose).toHaveBeenCalledOnce()
const failedSpawnCloseError = new Error('query close failed')
const failedSpawnClose = vi.fn(() => { throw failedSpawnCloseError })
const failedSpawnWithCloseFailure = fakeChild({
@@ -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: 18e805f0e0a1d8d33ba77182ed73d213beb62e07
README.zh.md: 7e376de43092b25c4206ee5e1cf5d736c2f1c910
README.md: 3d59ca1eaf3db9dd9d9d2cd451692ebd2a956ef4
README.zh.md: abff7b569e2ce8261366c004ab6d03ea53300fdb
+2 -13
View File
@@ -27,26 +27,15 @@ 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.
This package is an optional Profile Bundle. Install it into the target Profile, then restart that Profile; its declared `cordis.patch.yml` layer registers only the dormant `codex` Host provider and starts no Codex process. Removing the package withdraws that provider on the next Profile start.
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh --profile <name>
```
Installation controls Host availability, not model permission. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_codex` only to new agents composed from the copy. The Profile's own patch can replace the Bundle row's complete `config`, while a custom Host composition can still mount the package 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. A custom host composition can still use both rows directly.
```yaml
# $DSH_HOME/profiles/<name>/cordis.patch.yml (optional provider override)
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
config:
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
```
```yaml
# A copied Agent Preset; remove `disabled` to grant this tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
+2 -13
View File
@@ -27,26 +27,15 @@
生产环境会从 `PATH` 中解析 `codex`,并使用宿主机原生的 Codex 配置与身份验证。本插件不安装 Codex、不选择模型、不创建 `CODEX_HOME`、不执行登录,也不探测版本。子进程 seam 会移除具有凭证特征的环境变量,因此供子进程使用的 API 密钥必须在 `env` 中显式提供;除非被覆盖,`PATH``HOME` 等普通环境变量值仍然可用。
本包是可选的 Profile Bundle。将它安装进目标 Profile 后重启该 Profile;包所声明的 `cordis.patch.yml` 层只注册休眠的 `codex` Host provider,不会启动 Codex 进程。移除该包后,下一次 Profile 启动会撤回这一 provider
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh --profile <name>
```
安装决定 Host 可用性,而不是模型权限。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的新 agent 暴露 `subagent_codex`。Profile 自己的 patch 可以替换 Bundle 行的完整 `config`,而自定义 Host 组合仍可直接挂载本包。
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Codex 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_codex`。自定义宿主组装仍可直接使用两条配置行
```yaml
# $DSH_HOME/profiles/<name>/cordis.patch.yml (optional provider override)
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
config:
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
```
```yaml
# A copied Agent Preset; remove `disabled` to grant this tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
@@ -1,6 +0,0 @@
# This optional Profile layer registers the dormant Codex provider. Agent
# presets separately decide whether one session receives its delegation tool.
- insert:
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
@@ -28,18 +28,13 @@
"files": [
"lib/index.js",
"lib/invariant.js",
"cordis.patch.yml",
"lib/types/**/*.d.ts"
],
"license": "MIT",
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
}
},
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-sdk-protocol": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
@@ -47,7 +42,6 @@
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/dsh-sdk-protocol": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
@@ -56,6 +50,7 @@
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-loader-smoke": "workspace:^",
"@deepseek-ai/dsh-sdk-protocol": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
@@ -1,4 +1,3 @@
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
@@ -13,13 +12,6 @@ const fixtureDir = fileURLToPath(new URL(
))
const driver = join(fixtureDir, 'driver.ts')
const configPath = join(fixtureDir, 'cordis.yml')
const packageDir = fileURLToPath(new URL('..', import.meta.url))
const manifest = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')) as {
dsh?: { bundle?: { patch?: string } }
}
const bundlePatch = manifest.dsh?.bundle?.patch
if (bundlePatch === undefined) throw new Error('Codex package must declare a Bundle patch')
const bundlePatchPath = join(packageDir, bundlePatch)
const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
describe('Codex provider public Loader composition', () => {
@@ -30,7 +22,6 @@ describe('Codex provider public Loader composition', () => {
binScript: driver,
libBinScript: driver,
configPath,
binArgs: [configPath, bundlePatchPath],
tsconfigPath: repoTsconfig,
env: {
// Loading the optional package must not probe or start a Codex binary.
@@ -1,10 +1,6 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { PassThrough } from 'node:stream'
import { fileURLToPath } from 'node:url'
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import * as yaml from 'js-yaml'
import { describe, expect, it, vi } from 'vitest'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
@@ -264,31 +260,6 @@ function turnCompleted(
}
describe('task admission and package contracts', () => {
it('ships one independently installable provider-only Bundle patch', () => {
const root = fileURLToPath(new URL('..', import.meta.url))
const manifest = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')) as {
dependencies?: Record<string, string>
peerDependencies?: Record<string, string>
files?: string[]
dsh?: { bundle?: { patch?: string } }
}
expect(manifest.dsh?.bundle?.patch).toBe('./cordis.patch.yml')
expect(manifest.files).toContain('cordis.patch.yml')
expect(manifest.dependencies).toHaveProperty('@deepseek-ai/dsh-sdk-protocol')
expect(manifest.peerDependencies).not.toHaveProperty('@deepseek-ai/dsh-sdk-protocol')
expect(manifest.dependencies).not.toHaveProperty('@deepseek-ai/dsh-subagent-claude-code')
const parsed = yaml.load(readFileSync(resolve(root, manifest.dsh!.bundle!.patch!), 'utf8'))
const rows = Array.isArray(parsed)
? (parsed as Array<{ insert?: Array<{ id?: string; name?: string }> }>).flatMap(entry => entry.insert ?? [])
: []
expect(rows).toEqual([{
id: 'subagent-codex',
name: '@deepseek-ai/dsh-subagent-codex',
}])
expect(JSON.stringify(rows)).not.toContain('tool-subagent')
})
it('resolves the fixed app-server command through the Windows npm shim boundary', () => {
expect(codexAppServerArgv('win32')).toEqual([
'cmd.exe',
+3 -3
View File
@@ -7114,9 +7114,6 @@ importers:
packages/subagent/subagent-codex:
dependencies:
'@deepseek-ai/dsh-sdk-protocol':
specifier: workspace:^
version: link:../../sdk/protocol
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -7139,6 +7136,9 @@ importers:
'@deepseek-ai/dsh-loader-smoke':
specifier: workspace:^
version: link:../../test-support/loader-smoke
'@deepseek-ai/dsh-sdk-protocol':
specifier: workspace:^
version: link:../../sdk/protocol
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../core/session
@@ -14,7 +14,7 @@ describe('configuration source ownership gate', () => {
it('rejects inline endpoints in shipped bundle patches', () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-config-source-ownership-'))
roots.push(root)
const directory = join(root, 'packages/subagent/subagent-codex')
const directory = join(root, 'packages/subagent/subagent-claude-code')
mkdirSync(directory, { recursive: true })
writeFileSync(
join(directory, 'cordis.patch.yml'),
@@ -22,7 +22,7 @@ describe('configuration source ownership gate', () => {
)
expect(collectConfigSourceOwnershipViolations(root)).toEqual([
'packages/subagent/subagent-codex/cordis.patch.yml:2: inlines a credential or endpoint from the environment.'
'packages/subagent/subagent-claude-code/cordis.patch.yml:2: inlines a credential or endpoint from the environment.'
+ ' The adapter resolves apiKeyEnv through ctx.credentials and the endpoint through the'
+ ' environment snapshot; inlining here bypasses both ladders.',
])
+1 -6
View File
@@ -117,17 +117,12 @@ describe('workspace Bundle discovery and product dependency closures', () => {
])
})
it('keeps the default and two optional product closures independent', () => {
it('keeps the default and optional Claude Code closure independent', () => {
const shipped = productionClosure('@deepseek-ai/dsh')
expect(shipped).not.toContain('@deepseek-ai/dsh-subagent-codex')
expect(shipped).not.toContain('@deepseek-ai/dsh-subagent-claude-code')
expect(shipped).not.toContain('@anthropic-ai/claude-agent-sdk')
const codex = productionClosure('@deepseek-ai/dsh-subagent-codex')
expect(codex).toContain('@deepseek-ai/dsh-sdk-protocol')
expect(codex).not.toContain('@deepseek-ai/dsh-subagent-claude-code')
expect(codex).not.toContain('@anthropic-ai/claude-agent-sdk')
const claudeCode = productionClosure('@deepseek-ai/dsh-subagent-claude-code')
expect(claudeCode).toContain('@anthropic-ai/claude-agent-sdk')
expect(claudeCode).not.toContain('@deepseek-ai/dsh-subagent-codex')