dshkit

Call Claude Code and Codex as sub-agents from DeepSeek Harness

dsh ships six sub-agent providers, including real Claude Code (via the official Agent SDK) and real Codex (via codex app-server). The exact config rows, why the tools ship disabled, credential scrubbing, and what the child does and does not inherit.

Updated 2026-08-145 min
Short answer

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:

PackageWhat the child is
dsh-subagent-spawn-in-processA fresh in-process child
dsh-subagent-fork-in-processAn in-process child started from the parent's completed history
dsh-subagent-acpAn out-of-process child over ACP
dsh-subagent-codexA real Codex app-server child
dsh-subagent-claude-codeA real Claude Code child, through the official Claude Agent SDK
dsh-subagent-dsh-sdkAn 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-managed

Two 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.

ConfigDefaultMeaning
env{}Explicit SDK/CLI environment layered over the credential-scrubbed parent environment
disposeGraceMs3000Grace 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 initializeinitializedthread/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-managed

The 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.

Keep reading