The dsh-subagent-claude-code and dsh-subagent-codex packages register real providers that drive your installed claude and codex binaries. Both ship loaded but with their tool row disabled: true in the full agent presets — copy a preset and remove that field to expose subagent_claude_code or subagent_codex. Credential-shaped environment variables are scrubbed, so the child's API key must be passed explicitly under config.env.
The claim that DeepSeek Harness can drive Claude Code and Codex as sub-agents circulated widely in
the first week without anyone showing the configuration. It is true, it is first-party, and the
packages are @deepseek-ai/dsh-subagent-claude-code and @deepseek-ai/dsh-subagent-codex.
Here is what they actually do.
The provider family
Delegation is a seam — ctx.subagents — and multiple named providers can coexist in one
context. What ships:
| Package | What the child is |
|---|---|
dsh-subagent-spawn-in-process | A fresh in-process child |
dsh-subagent-fork-in-process | An in-process child started from the parent's completed history |
dsh-subagent-acp | An out-of-process child over ACP |
dsh-subagent-codex | A real Codex app-server child |
dsh-subagent-claude-code | A real Claude Code child, through the official Claude Agent SDK |
dsh-subagent-dsh-sdk | An out-of-process Harness child via the TypeScript SDK |
dsh-tool-subagent is what exposes any of them to the model, and the same tool package is
instantiated once per provider you want to surface.
Claude Code
The provider invokes the official Claude Agent SDK in the delegating session's workspace,
resolves the native claude executable through the shared subprocess service, submits one
self-contained text task, and returns only the final answer.
- 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-managedTwo rows: the provider and the tool that exposes it. Note !!js — a YAML tag that evaluates
a JavaScript expression, here reading the host's environment.
It uses your real Claude installation. The provider deliberately omits the SDK's
settingSources option, so the official SDK reads your normal user, project and local Claude
settings relative to the parent session's cwd, including native account state. It neither copies nor
filters those files, and does not create or modify login state.
Each query sets persistSession: false and disables AskUserQuestion. No canUseTool, elicitation
or dialog callback is supplied — so an unattended interaction fails through the SDK rather than
hanging on a user interface this provider does not own.
| Config | Default | Meaning |
|---|---|---|
env | {} | Explicit SDK/CLI environment layered over the credential-scrubbed parent environment |
disposeGraceMs | 3000 | Grace in ms between process-tree termination tiers; disposal then waits for whole-tree exit |
The runtime dependency is pinned to @anthropic-ai/[email protected].
Codex
The provider starts codex app-server --stdio in the delegating session's workspace, performs
initialize → initialized → thread/start { cwd, ephemeral: true }, submits one task, and waits
for the authoritative turn/completed notification.
- 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-managedThe approval handling is the interesting part. Because the child is unattended, the provider
selects a non-approval decision offered by the request, preferring cancel; where the request
shape offers no decision list it falls back to decline. It answers permission requests with an
empty turn-scoped permission set, answers user-input requests with no answers, and declines MCP
elicitation. A request with no legal unattended response fails the run.
In other words: the Codex child cannot be talked into approving anything. That is a deliberate safety property, and it also means a task requiring approval will fail rather than proceed.
Failure mapping is precise: a failed turn whose codexErrorInfo is contextWindowExceeded maps to
max-tokens; every other remote interruption maps to error; local cancellation maps to aborted.
The provider never produces refusal.
Development evidence is pinned to @openai/[email protected], but the npm package is a test-only
dependency — deployments still supply codex on PATH.
The three things that will trip you up
The tool ships disabled
This is why "I installed it and there is no subagent_claude_code tool" is the expected experience,
not a bug.
Your API key is scrubbed unless you pass it explicitly
Credential-shaped ambient variables are removed before the explicit env overlay is applied. So
even if ANTHROPIC_API_KEY is exported in your shell, the child does not see it unless you put it
in config.env — which is exactly what the !!js process.env.ANTHROPIC_API_KEY line does.
Non-credential variables survive: ANTHROPIC_BASE_URL, PATH, HOME and other ordinary ambient
values remain inherited unless overridden.
The child inherits nothing conversational
Both providers report inheritsParentContext: false. The child receives:
- the standalone text task
- the parent session's cwd
It does not receive the parent conversation, persona, tool filter, depth policy, or structured-output contract. Every run gets an independent process, cancellation controller and non-persisted product session.
Design accordingly: the task string is the entire briefing. If the child needs context, put it in the task or in the shared workspace — the workspace is the only channel.
Lifecycle
dispose() is idempotent on both providers. It aborts the run, closes the protocol wire (Codex also
attempts a best-effort turn/interrupt), invokes the shared process-tree termination escalation, and
waits for whole-tree exit.
Note the discipline: graceful close expresses protocol intent, but the subprocess handle remains the authority for process quiescence. A provider that only asked nicely would leak processes. Result failure and teardown failure are tracked separately.
Why this matters more than it looks
Sub-agent providers are ordinary plugins registering on a seam. That means routing a step to a different vendor's agent is a configuration decision, not an integration project — and the harness stays the orchestrator regardless of whose model does the work.
It also means the honest comparison between these tools is not "which one wins". You can run the DeepSeek harness as the loop and Claude Code as a specialist inside it, which is a legitimate architecture rather than a hedge. See DeepSeek Harness vs Claude Code for where each one's strengths actually lie.
Frequently asked
Does the Claude Code child see my conversation?
No. Both providers report inheritsParentContext: false. The child receives a standalone text task and the parent session's cwd — not the parent conversation, persona, tool filter, depth policy or structured-output contract.
Why is the tool not available by default?
The provider loads on the host, but the full agent presets carry the tool row with disabled: true, and no product process starts until a tool call. Copy a preset and remove that field to expose the tool only to agents composed from your copy.
Which account does the child use?
The host's native installation. The Claude Code provider deliberately omits the SDK settingSources option, so the official SDK reads your normal user, project and local Claude settings. Neither plugin installs a CLI, selects a model, creates a product home, logs in, or probes an account.
Why is my API key not reaching the child?
Credential-shaped ambient variables are removed before the explicit env overlay is applied. A key intended for the child must be supplied under config.env. Non-credential variables such as ANTHROPIC_BASE_URL, PATH and HOME remain inherited.