Back to home

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 initializeinitializedthread/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 notificationProjection-session events
item/agentMessage/deltaaccumulated 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 settlementturn/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

KeyDefaultMeaning
env{}Explicit child environment layered over the subprocess seam's credential-scrubbed parent environment.
disposeGraceMs3000Positive finite grace in milliseconds, no greater than MAX_TIMER_DELAY_MS, between the shared process-tree owner's termination tiers.
ephemeraltrueThread 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/delta stream is accumulated but written as one assistant/message on item/completed; the Web UI does not stream the answer live. Streaming assistant/chunk projection is deferred.
  • Tool projection covers commandExecution and fileChange items onlymcpToolCall, webSearch, collabToolCall, plan, and reasoning items 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 resume through the harness are out of scope (persisting the thread with ephemeral: false makes 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.