Bring Claude Code's Workflow tool — "ultracode", deterministic multi-agent orchestration — to
OpenAI Codex. This repo holds a verified TypeScript implementation on @openai/codex-sdk (with an
in-session MCP-server front door), the original Python kit, and the analysis behind both.
Status: TS port verified green —
tsc+ 18/18 offline tests + live preflight + live engine smoke + MCP tool fan-out end-to-end (oneultracode(n=2)call → 2 child codex workers, network retained, synthesis + ledger). See Verification.
Claude Code's "dozens of agents" don't come from the model deciding to spawn them — they come from a
deterministic harness that holds the concurrency number in code (parallel() / pipeline() loops). Codex's
in-session spawn_agent is the opposite: model-driven and trained to be conservative (the docs say it "only
spawns a new agent when you explicitly ask," and the canonical examples spawn 2–6). No prompt reliably gets it to
dozens.
So this project holds the concurrency number in code and uses Codex as the agent primitive:
- Engine —
agent()/parallel()(barrier, all-settled →null) /pipeline()(no barrier) + an output-token budget ceiling, worktree isolation for parallel writers, structured-output schemas, and a run ledger. - Methodology — the patterns that make it ultracode rather than a parallel
map: adversarial verify, judge panel (synthesize + graft), multi-modal sweep, loop-until-{dry,count,budget}, completeness critic, review-then-verify. - MCP front door (TS) — register the orchestrator as an MCP server; the in-session model calls
ultracode(task, n?)as one tool, and the server (a sibling of the harness, outside the per-command sandbox, so it keeps network) deterministically fans out N workers. This is what makes deterministic fan-out an in-session primitive — source-verified againstcodex-rs/rmcp-client/src/stdio_server_launcher.rs.
| Path | What |
|---|---|
ts/ |
The TypeScript port on @openai/codex-sdk + the MCP-server front door. The latest, verified implementation. See ts/README.md. |
codex_workflow.py / codex_patterns.py |
The original Python kit (driving codex exec subprocesses). |
docs/parity-with-claude-ultracode.md |
Gap analysis vs Claude Code's ultracode — what's at parity, what's closable, what's architectural. |
workflow-in-codex.md |
Design notes + the original parity write-up. |
ultracode-codex-setup.md |
The in-session native-skill setup (spawn_agent / spawn_agents_on_csv). |
ultracode-test-plan.md |
Test plan for the native-skill kit. |
review-findings.md |
Adversarial review of the global AGENTS.md. |
cd ts
npm install
npm run typecheck # tsc --noEmit
npm test # vitest — offline suite (SDK stubbed, no network/tokens) — 18/18
npx tsx src/preflight.ts # CodexEnv probe — run once before live use (needs codex auth + tokens)preflight.ts round-trips the real SDK↔CLI contract (resume identity, mcp_servers={} + --output-schema,
usage.output_tokens, effort override). Don't trust resume/budget until it passes.
import * as wf from "./src/codexWorkflow.js";
import { z } from "zod";
// Fan out — barrier; a failed agent becomes null (never aborts the batch).
const Answer = z.object({ answer: z.number() });
const results = await wf.parallel([
() => wf.agent('Compute 6*7. Return JSON {"answer": <number>}.', { schema: Answer }),
() => wf.agent('Compute 8*9. Return JSON {"answer": <number>}.', { schema: Answer }),
]);
// results: [{answer:42}, {answer:72}]
// Pipeline — NO barrier; each item flows through all stages independently.
// stage(prev, originalItem, index); a throwing stage drops that item to null.
const out = await wf.pipeline(files, review, verify);
// Budget — output-token ceiling (env CODEX_WF_BUDGET; unset = unlimited).
wf.budget.total; wf.budget.spent(); wf.budget.remaining();
// Worktree isolation — the agent edits files only; the orchestrator (out of sandbox) runs git,
// auto-cleans the worktree+branch, and collects the diff.
await wf.agent("Add a subtract() function to mathlib.ts", { isolation: "worktree", cwd: repo });
wf.collectedDiffs(); // [{ branch, diff }]
// Run ledger
wf.startRun("my-run"); wf.saveResult("findings", obj); wf.writeLedger({ Summary: "…" });Schema notes: pass a Zod schema (preferred — validated client-side) or a plain JSON Schema. Caller-optional
fields must be z.T().nullable() (not .optional()); the engine widens them so an OpenAI-strict schema can
still let the model emit null.
import * as p from "./src/codexPatterns.js";
// N independent skeptics try to REFUTE; survives only by majority (tie = kill).
await p.adversarialVerify("divide(x,0) raises", { lenses: ["correctness", "security", "reproduce"] });
// N attempts → judge scores → synthesize from the winner WHILE grafting runner-up ideas.
await p.judgePanel("Design a cache layer", ["LRU-first", "write-through", "TTL-first"], OutSchema);
// Each agent searches a DIFFERENT way, then dedupe-union.
await p.multiModalSweep(["name", "content", "caller", "commit-history"], "the repo", { schema: Found });
// Discovery loops: stop on K dry rounds / target count / token budget.
await p.loopUntilDry(finder, key);
await p.reviewThenVerify(["bugs", "perf", "security"], FindSchema); // reviewer per dimension, each finding verifiedBuild, then register in ~/.codex/config.toml (auth/PATH must be explicit — the launcher does env_clear):
cd ts && npm run build # produces dist/mcpServer.js[mcp_servers.ultracode]
command = "/opt/homebrew/bin/node" # absolute — launches regardless of curated PATH
args = ["/abs/path/to/ts/dist/mcpServer.js"]
[mcp_servers.ultracode.env]
HOME = "/Users/you"
CODEX_HOME = "/Users/you/.codex" # so the server + its child codex runs find auth.json
PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"Then in any codex session, the model can call:
ultracode(task, n?)— fan out N read-only workers + synthesize; returns{ run_id, workers, result }.ultracode_review(dimensions, target?)— reviewer per dimension, each finding adversarially verified.workflow_status(run_id)— read the run ledger.
Headless caveat (account-level): under non-interactive
codex exec, a cloud-forcedOnRequestapproval auto-cancels the tool call (codex issue #24135). In an interactive codex session you approve it once and the fan-out proceeds (children run outside the sandbox, keeping network). You can also drive the tool directly withnpm run mcp:call— the exact server code path, end-to-end.
import codex_workflow as wf
import codex_patterns as cp
res = wf.parallel([lambda: wf.agent("...", schema=S), lambda: wf.agent("...", schema=S)])
cp.adversarial_verify("claim", lenses=("correctness", "security"))
cp.review_then_verify(["bugs", "perf"], find_schema)Knobs via env: CODEX_WF_CONCURRENCY, CODEX_WF_MODEL, CODEX_WF_EFFORT, CODEX_WF_CWD, CODEX_WF_BUDGET.
| Env | Default | Meaning |
|---|---|---|
CODEX_WF_MODEL |
gpt-5.5 |
model for agents |
CODEX_WF_EFFORT |
medium |
reasoning effort (minimal/low/medium/high/xhigh) |
CODEX_WF_CONCURRENCY |
min(16, cores-2) |
max concurrent child codex processes |
CODEX_WF_CWD |
$PWD |
base working dir / git repo for worktrees + ledger |
CODEX_WF_BUDGET |
unset = unlimited | output-token ceiling (agents throw once reached) |
CODEX_WF_MCP_CLEAN |
1 |
run child agents MCP-clean (mcp_servers={}) |
CODEX_WF_ARGS |
— | JSON value exposed to the script as args |
Full analysis in docs/parity-with-claude-ultracode.md. Summary:
| Capability | Status | Note |
|---|---|---|
agent / parallel / pipeline semantics, schema output, concurrency cap |
at parity | + a 1000-agent lifetime cap and 4096-item batch cap |
| Quality patterns (verify / judge / sweep / loops / critic / review) | at parity | full methodology ported (TS + Python) |
| In-session deterministic fan-out that keeps network | closable | via the MCP-server front door (source-verified) |
| Structured token usage | at parity / better | from the SDK's turn.usage, not footer-scraping |
| Resume | partial | resumeThread + a read-only content-hash cache (not full cached-prefix replay) |
| Live background progress tree | partial | runStreamed data + ledger; no in-client /workflows tree |
| Writers run tests in-agent | blocked / spike | account cloud OnRequest forbids exec-mode shell; verification is orchestrator-side |
Per-agent model / effort override |
Codex advantage | richer than a fixed agentType registry |
The TypeScript port is verified green (2026-06-16): npm install · tsc --noEmit · 18/18 offline vitest ·
live preflight 4/4 · live engine smoke (parallel agents + worktree writer, no leak) · MCP stdio handshake ·
MCP tool fan-out end-to-end (one ultracode(n=2) call → 2 child codex workers that retained network →
synthesis + ledger). The Python kit is the earlier, separately-verified reference.
Re-run any of it:
cd ts
npm test # offline (no tokens)
npm run live # live engine smoke (needs codex auth + tokens)
npm run mcp:call # ultracode MCP tool, end-to-end (needs codex auth + tokens)