ivwumupy
dsh-better-codex-subagent
No description
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-better-codex-subagent
English | 中文
A drop-in replacement for the fixed codex subagent provider
(@deepseek-ai/dsh-subagent-codex)
that additionally mirrors the Codex app-server stream into a harness child
session. Every Codex run therefore renders as an ordinary one-shot subagent in
the session tree and Web UI: a discoverable child node under the delegating
session, with the delegated task, the assistant answers, and the tool
executions (commands and file changes) as a normal conversation transcript.
The provider registers under the same codex name, so the
tool-subagent-codex tool row (provider: codex) keeps working unchanged.
Loading this plugin together with @deepseek-ai/dsh-subagent-codex fails loud
on the duplicate provider name — compose one or the other.
Start and ownership
start(request) derives the child cwd from the parent Session, spawns the
fixed codex app-server --stdio command through dsh-subprocess, and performs
initialize → initialized → thread/start { cwd, ephemeral }. Only after
the app-server returns a thread matching the requested lifetime does the
provider create the projection child session, seed it (descriptor, turn start,
user task), and publish the run. A failure before publication closes the wire,
terminates the managed process tree, waits for it to exit, and rejects
start() — no orphan child session is left behind.
The published run's result contract is identical to the base provider: one
turn, the authoritative turn/completed terminal notification, and the
latest agentMessage with phase: "final_answer" (or the phase: null
compatibility fallback) as the returned answer. Local cancellation maps to
aborted, contextWindowExceeded to max-tokens, and any other remote
failure to error. dispose() is idempotent and reaches whole-tree
quiescence. The projection session stays in the store after the run settles
as a terminal, read-only one-shot child; the Web UI already renders one-shot
subagent conversations read-only.
Projection
The child session mirrors the app-server notification stream through a stateful transcript:
| App-server notification | Projection-session events |
|---|---|
item/agentMessage/delta | accumulated per item id |
item/completed (agentMessage, final_answer or null phase) | assistant/message with the accumulated or completed text |
item/started / item/completed (commandExecution) | tool/call (exec_command with command/cwd) + tool/result (aggregatedOutput, else exit code, else status) |
item/started / item/completed (fileChange) | tool/call (fileChange with path/kind changes) + tool/result (path summary) |
| run settlement | turn/end (completed / aborted / max-tokens / error) |
The transcript is deliberately tolerant: unknown notification methods, item kinds, and agent-message phases are ignored, so an app-server version that introduces new item types cannot fail the run. Malformed payloads of known kinds degrade to a skipped write. The base provider's fail-closed posture is kept where it matters: unknown server requests (which demand an answer) and mismatched thread/turn scoping in terminal notifications still fail the run.
Configuration
| Key | Default | Meaning |
|---|---|---|
env | {} | Explicit child environment layered over the subprocess seam's credential-scrubbed parent environment. |
disposeGraceMs | 3000 | Positive finite grace in milliseconds, no greater than MAX_TIMER_DELAY_MS, between the shared process-tree owner's termination tiers. |
ephemeral | true | Thread lifetime passed to thread/start. false persists the Codex session under CODEX_HOME/sessions for a later codex resume; true keeps the base provider's ephemeral contract. |
- id: better-codex-subagent
name: 'dsh-better-codex-subagent'
config:
env:
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
ephemeral: false
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: codex
toolName: subagent_codex
maxDepth: provider-managed
Background execution
One-shot background execution works for the codex provider: the tool row
just needs enableRunInBackground unset or true (the default). The model
can then pass run_in_background: true, which starts the Codex run as a
background job that returns a job id immediately; job_output collects the
final answer and job_kill cancels. The projection session is the job's
child session, so the Web UI still renders the running and settled subagent
transcript. backgroundMode must stay one-shot (the default): the codex
provider has no prepareContinuable capability, and a continuable tool row
fails at mount.
Installation
The package is a dsh bundle: its manifest declares
dsh.bundle with a patch layer (cordis.patch.yml) that mounts the provider,
so dsh plugin installs it as a bundle into a profile. The package ships
prebuilt (lib/types/*.js ESM + declarations) plus the source and declares
its harness packages as peer dependencies, so it loads wherever the harness
packages are already present — a dsh profile, for example, resolves them from
the installed dsh runtime.
# From this package directory: build and produce the publishable tarball.
pnpm pack # → dsh-better-codex-subagent-0.1.0.tgz
Install into a profile (dsh plugin forwards to pnpm and appends the bundle
to the profile's dsh.profile.bundles because of the dsh.bundle manifest):
dsh plugin --profile web add ./dsh-better-codex-subagent-0.1.0.tgz
Other distribution forms work the same way: dsh plugin --profile web add dsh-better-codex-subagent after publishing to npm, or
dsh plugin --profile web add github:you/deepseek-harness#<sha> from git — a
git install fetches sources only, so the package then needs a prepare
script (none is shipped yet) and an allowBuilds entry in the profile's
pnpm-workspace.yaml.
If the profile already mounts the base
@deepseek-ai/dsh-subagent-codex provider (for example a manually inserted
- id: subagent-codex row in the profile's cordis.patch.yml), remove that
row and its dependency before restarting — both packages register the codex
provider name and the duplicate fails loud:
dsh plugin --profile web remove @deepseek-ai/dsh-subagent-codex
# and delete the `- id: subagent-codex` insert row from
# ~/.dsh/profiles/web/cordis.patch.yml
The tool-subagent-codex tool row (provider: codex) in the standard-codex
agent preset needs no change. Then restart dsh web.
Product compatibility and evidence
The wire implements the same 0.147.0 app-server surface as the base provider
(adapted from its wire.ts), with the ephemeral lifetime parameterized and a
validated notification sink added. Development evidence is pinned to
@openai/codex@0.147.0; the npm package is a test-only dependency, and
deployments still supply codex on PATH. The keyless real-product test
boots the real app-server against a loopback Responses fixture and asserts
that the published run is discoverable through ctx.subagents.listChildren
and that its transcript carries the delegated task, the answer, and the
closing turn/end.
Known Limitations and Deferred Work
- Message granularity is per completed item, not per delta — the
item/agentMessage/deltastream is accumulated but written as oneassistant/messageonitem/completed; the Web UI does not stream the answer live. Streamingassistant/chunkprojection is deferred. - Tool projection covers
commandExecutionandfileChangeitems only —mcpToolCall,webSearch,collabToolCall,plan, andreasoningitems are ignored; the transcript is the extension point for them. - No continuation — the child is a terminal one-shot projection session;
follow-up messages, cold resume, and
codex resumethrough the harness are out of scope (persisting the thread withephemeral: falsemakes the Codex session resumable in the Codex CLI itself). - Host-managed product installation and account state — a missing or
incompatible
codex, configuration error, or authentication failure is surfaced as a startup or run error; the plugin provides no installer, login flow, or runtime version gate. - No wall-clock timeout or side-effect rollback — the caller cancels long work, and files or external systems changed before cancellation are not restored.