dsh-subagent-claude-code 和 dsh-subagent-codex 这两个包注册的是真实 provider,驱动你本机装好的 claude 和 codex。两者默认都会加载,但完整 agent 预设里对应的工具行是 disabled: true——复制一份预设、删掉那个字段,才会暴露出 subagent_claude_code 或 subagent_codex。凭证形态的环境变量会被清洗掉,所以给子 agent 的 API key 必须显式写在 config.env 里。
「DeepSeek Harness 能把 Claude Code 和 Codex 当子 agent 调用」这个说法在第一周传得很广,但没人拿出过
配置。这件事是真的,而且是第一方的,包名是 @deepseek-ai/dsh-subagent-claude-code 和
@deepseek-ai/dsh-subagent-codex。
下面是它们实际在干什么。
Provider 家族
委派是一个 seam——ctx.subagents——而且同一个上下文里可以并存多个具名 provider。自带这些:
| 包 | 子 agent 是什么 |
|---|---|
dsh-subagent-spawn-in-process | 全新的进程内子 agent |
dsh-subagent-fork-in-process | 从父级已完成历史起步的进程内子 agent |
dsh-subagent-acp | 经 ACP 的进程外子 agent |
dsh-subagent-codex | 真实的 Codex app-server 子进程 |
dsh-subagent-claude-code | 真实的 Claude Code 子进程,走官方 Claude Agent SDK |
dsh-subagent-dsh-sdk | 经 TypeScript SDK 的进程外 Harness 子 agent |
而 dsh-tool-subagent 才是把它们暴露给模型的那个包——你想暴露几个 provider,就实例化几次这个工具包。
Claude Code
provider 在委派方会话的工作区里调用官方 Claude Agent SDK,通过共享的 subprocess 服务解析出原生
claude 可执行文件,提交一段自包含的文本任务,只把最终答案返回。
- id: subagent-claude-code
name: '@deepseek-ai/dsh-subagent-claude-code'
config:
env:
ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
maxDepth: provider-managed两行:一行 provider,一行把它暴露出来的工具。注意 !!js——这是一个 YAML 标签,会求值一段
JavaScript 表达式,这里用来读宿主的环境变量。
它用的是你真实的 Claude 安装。 provider 刻意省略了 SDK 的 settingSources 选项,所以官方 SDK
会相对父会话的 cwd 去读你正常的用户级、项目级、本地 Claude 设置,包括原生账号状态。它既不复制也不过滤
这些文件,也不创建或修改登录状态。
每次查询都设 persistSession: false 并禁用 AskUserQuestion。不提供 canUseTool、elicitation 或
对话回调——所以无人值守场景下的交互会通过 SDK 直接失败,而不是卡在一个这个 provider 并不拥有的
用户界面上。
| 配置 | 默认 | 含义 |
|---|---|---|
env | {} | 显式的 SDK/CLI 环境,叠加在被清洗过凭证的父环境之上 |
disposeGraceMs | 3000 | 进程树终止各档之间的宽限毫秒数;之后等待整棵树退出 |
运行时依赖锁定在 @anthropic-ai/[email protected]。
Codex
provider 在委派方会话的工作区里启动 codex app-server --stdio,走
initialize → initialized → thread/start { cwd, ephemeral: true },提交一个任务,然后等待权威的
turn/completed 通知。
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
config:
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
maxDepth: provider-managed有意思的是它怎么处理审批。因为子 agent 无人值守,provider 会在请求提供的选项里挑一个「非批准」的
决定,优先选 cancel;如果请求形态里没有可选决定列表,就退回 decline。它用一个空的、turn 作用域
的权限集合回答权限请求,用「无答案」回答用户输入请求,并拒绝 MCP elicitation。一个没有合法无人值守
回应的请求会让整次运行失败。
换句话说:这个 Codex 子 agent 没法被说服去批准任何东西。 这是刻意的安全属性,同时也意味着需要审批 的任务会失败,而不是硬着头皮往下走。
失败映射也很精确:失败 turn 的 codexErrorInfo 是 contextWindowExceeded 时映射成 max-tokens;
其它远端中断一律 error;本地取消映射成 aborted。这个 provider 永远不产生 refusal。
开发期证据锁定在 @openai/[email protected],但那个 npm 包是仅测试用依赖——部署时仍然需要你自己在
PATH 上提供 codex。
三个一定会绊到你的点
工具默认是关的
所以「我装好了但没有 subagent_claude_code 这个工具」是预期行为,不是 bug。
不显式传,你的 API key 会被清洗掉
凭证形态的环境变量会在显式 env 覆盖之前被移除。 所以即使你在 shell 里 export 了
ANTHROPIC_API_KEY,子 agent 也看不到——除非你把它写进 config.env,而这正是
!!js process.env.ANTHROPIC_API_KEY 那一行在做的事。
非凭证类变量会活下来:ANTHROPIC_BASE_URL、PATH、HOME 等普通环境值仍然被继承,除非你覆盖它们。
子 agent 在对话层面什么都不继承
两个 provider 都报告 inheritsParentContext: false。子 agent 收到的是:
- 那段独立的文本任务
- 父会话的 cwd
它不会收到父对话、人设、工具过滤、深度策略或结构化输出契约。每次运行都有独立的进程、取消控制器, 以及不持久化的产品会话。
据此设计:任务字符串就是全部的交底。 子 agent 需要上下文,就把它写进任务里,或者放进共享工作区 ——工作区是唯一的通道。
生命周期
两个 provider 的 dispose() 都是幂等的:中止运行、关闭协议通道(Codex 还会尽力发一次
turn/interrupt)、触发共享的进程树终止升级,然后等待整棵树退出。
注意这里的纪律:优雅关闭表达的是协议意图,但进程静默与否以 subprocess 句柄为准。 一个只会「好好 商量」的 provider 会漏进程。结果失败和拆卸失败是分开记的。
这件事比看起来更重要
子 agent provider 就是注册在某个 seam 上的普通插件。这意味着把某一步路由给另一家厂商的 agent 是一个 配置决策,不是一个集成项目——而且不管活是谁的模型干的,harness 始终是那个编排者。
这也意味着这些工具之间诚实的比较不是「谁赢」。你完全可以让 DeepSeek 的 harness 当主循环、把 Claude Code 当成里面的专家——这是正经架构,不是骑墙。各自强在哪,见 DeepSeek Harness vs Claude Code。
常见问题
Claude Code 子 agent 能看到我的对话吗?
看不到。两个 provider 都报告 inheritsParentContext: false。子 agent 收到的是一段独立的文本任务和父会话的 cwd——不包括父对话、人设、工具过滤、深度策略或结构化输出契约。
为什么这个工具默认用不了?
provider 会在宿主上加载,但完整 agent 预设里的工具行带着 disabled: true,而且在工具被调用之前不会启动任何产品进程。复制一份预设、删掉那个字段,工具就只对用你这份副本组装出来的 agent 可见。
子 agent 用的是哪个账号?
宿主上的原生安装。Claude Code provider 刻意省略了 SDK 的 settingSources 选项,所以官方 SDK 会读你正常的用户级、项目级和本地 Claude 设置。两个插件都不装 CLI、不选模型、不创建产品目录、不登录、也不探测账号。
为什么我的 API key 传不到子 agent?
凭证形态的环境变量会在显式 env 覆盖之前被移除。要给子 agent 的 key 必须写在 config.env 下。非凭证类变量如 ANTHROPIC_BASE_URL、PATH、HOME 仍然会被继承。