OpenAI ships an agentic coding CLI (codex, package @openai/codex). cuekit needs an adapter to spawn it as a delegation child, with the same UX guarantees the other pane adapters give:
- parents can attach to the live child pane via the multiplexer backend;
- parents can steer interactive children with
steer_task; - parents can opt into a
batchrun mode for short single-shot prompts; - child output is captured in the normal cuekit transcript path and exit-code sentinel;
- terminal status inference works the same way as
claude-code/antigravity; - permission-bypass behaves consistently with the existing
dangerously_skip_permissionssemantics.
The adapter ships alongside the existing claude-code / opencode / gemini / antigravity adapters; users pick agent_kind: codex per task or set it via project config / agent profile.
Probed against codex-cli 0.133.0 (codex --help + codex exec --help):
Usage: codex [OPTIONS] [PROMPT]
codex [OPTIONS] <COMMAND> [ARGS]
Commands:
exec Run Codex non-interactively (also accepts a [PROMPT] positional)
... (mcp, doctor, sandbox, plugin, ...)
Options:
-m, --model <MODEL>
-s, --sandbox <SANDBOX_MODE> read-only | workspace-write | danger-full-access
-a, --ask-for-approval <APPROVAL_POLICY> untrusted | on-failure | on-request | never
--dangerously-bypass-approvals-and-sandbox
--dangerously-bypass-hook-trust
-C, --cd <DIR>
--add-dir <DIR>
-i, --image <FILE>
-c, --config <key=value>
-p, --profile <CONFIG_PROFILE>
Compared to antigravity (agy) and gemini:
| capability | gemini flag | agy flag | codex flag | delta vs antigravity |
|---|---|---|---|---|
| batch (one-shot) | -p '<prompt>' |
-p '<prompt>' |
exec '<prompt>' (SUBCOMMAND) |
subcommand, not flag |
| interactive (REPL alive) | positional '<prompt>' |
-i '<prompt>' |
positional '<prompt>' |
positional, like gemini |
| permission bypass | -y |
--dangerously-skip-permissions |
--dangerously-bypass-approvals-and-sandbox |
longer flag name |
| restricted / sandbox | --approval-mode plan |
--sandbox (boolean) |
-s <mode> with value |
takes a value, 3 modes |
| model selection | -m '<model>' |
— | -m '<model>' |
present (antigravity lacks it) |
Implement createCodexAdapter as a normal createPaneAdapter consumer, mirroring the antigravity adapter's structure but with codex's flag set.
| Run mode | Launch shape |
|---|---|
interactive (default) |
codex [-s <mode> -a <policy> | -a <policy> | --dangerously-bypass-approvals-and-sandbox] [-m '<model>'] '<rendered prompt>' |
batch |
codex exec [...same flags...] '<rendered prompt>' |
Codex separates the SANDBOX axis (what the agent can do) from the APPROVAL axis (when codex pauses to ask the human). The single --dangerously-bypass-approvals-and-sandbox flag disables BOTH; setting -s alone disables only the sandbox part and leaves codex's default approval policy active — which can stall an unattended cuekit pane on a "please approve this command" prompt. The adapter therefore chooses between three exclusive flag groups:
adapter_options.sandboxset → emit-s <mode>AND-a <ask_for_approval ?? "never">, skip the bypass flag. Default-a neverkeeps the unattended pane unblocked; callers can override withadapter_options.ask_for_approval.adapter_options.ask_for_approvalset without sandbox → emit-a <policy>alone, skip the bypass flag. Codex's default sandbox stays.- Otherwise,
shouldDangerouslySkipPermissions(spec)true (default) → emit--dangerously-bypass-approvals-and-sandbox(bypasses both axes). Callers opt out withadapter_options.dangerously_skip_permissions: false.
The launch command still goes through wrapLaunchCommandWithExitCode so terminal status inference and the exit-code sentinel work the same way as claude-code.
The launch command still goes through wrapLaunchCommandWithExitCode so terminal status inference and the exit-code sentinel work the same way as claude-code.
{
agent_kind: "codex",
supports_steering: true,
supports_attach: true,
supports_model_selection: true, // ← divergence from antigravity
supports_artifacts: true,
supports_live_progress: false,
default_mode: "interactive",
supported_modes: ["interactive", "batch"],
}supports_model_selection: true is the load-bearing divergence from antigravity. Codex has -m <MODEL>, so model selection is real and we must advertise it correctly — the MCP list({ kind: "adapters" }) surface uses this to drive UX (model dropdowns, profile resolution, defaults attribution).
A pure builder lives in packages/adapters/src/codex-adapter.ts:
const CODEX_SANDBOX_MODES = ["read-only", "workspace-write", "danger-full-access"] as const;
const CODEX_APPROVAL_POLICIES = ["untrusted", "on-failure", "on-request", "never"] as const;
// `readSandboxMode` and `readApprovalPolicy` follow the same shape: parse the
// string value, validate against the enum, return undefined otherwise.
// Invalid input never produces a malformed launch command.
export function buildCodexLaunchCommand(spec: TaskSpec, options = {}): string {
const bin = options.codexBin ?? "codex";
const parts: string[] = [bin];
if (adapterRunModeFor(spec) === "batch") parts.push("exec");
const sandbox = readSandboxMode(spec);
const explicitApproval = readApprovalPolicy(spec);
if (sandbox) {
parts.push("-s", sandbox);
parts.push("-a", explicitApproval ?? "never");
} else if (explicitApproval) {
parts.push("-a", explicitApproval);
} else if (shouldDangerouslySkipPermissions(spec)) {
parts.push("--dangerously-bypass-approvals-and-sandbox");
}
if (spec.model) parts.push("-m", shellQuote(spec.model));
parts.push(shellQuote(renderTaskSpecPrompt(spec)));
return parts.join(" ");
}Notes:
- Batch mode is a subcommand (
codex exec), not a flag. The subcommand appears immediately after the binary name and BEFORE the options. Copy-pasting from antigravity (which uses-pflag for batch) will silently produce a broken command. adapter_options.sandboxaccepts a STRING value (read-only/workspace-write/danger-full-access), not a boolean. Invalid values are silently ignored — the adapter falls back to default-on bypass. This matches the soft-fail semantics adapter_options uses elsewhere (e.g. antigravity's=== truecheck).- The bypass flag is
--dangerously-bypass-approvals-and-sandbox— longer than claude-code's--dangerously-skip-permissionsand antigravity's--dangerously-skip-permissions. Rename will silently fail (codex rejects unknown flags). The adapter literally inlines the flag string to make grep-driven refactors safe.
These follow the shared pane adapter contract — nothing codex-specific. The smoke test must verify in a real runtime that:
tmux send-keysreliably appends a message to a running interactivecodexREPL prompt;codexdoes not eat or echo the trailing newline in a way that breaks subsequent steering;- transitioning between
codex's busy state and prompt-ready state does not require a special pre-keystroke before send-keys.
If (3) turns out to be necessary, follow the pattern opencode uses (a small pre-key sequence before the message), not a codex-specific bespoke path.
codex requires an authenticated OpenAI account. The adapter assumes the binary is already authenticated (via codex login). cuekit doctor probes codex --version and surfaces a clear hint when missing; it does not attempt to write or refresh credentials.
For users registering cuekit as an MCP server in codex itself, codex ships its own codex mcp add <name> -- <command> CLI that manages ~/.codex/config.toml directly:
codex mcp add cuekit -- cuekit --mcpThe cuekit dispatcher does NOT wrap this — codex's own CLI handles the TOML config correctly, and there's no hyphenated-path pitfall like antigravity has.
- Create
packages/adapters/src/codex-adapter.ts. - Export
createCodexAdapterandbuildCodexLaunchCommandfrompackages/adapters/src/index.ts. - Register the adapter in
packages/adapters/src/build-registry.tsalongside the other six. - Add
packages/adapters/__tests__/codex-adapter.test.tscovering build/launch and capability shape. - Update
cuekit doctorADAPTER_EXECUTABLEStable to include{ kind: "codex", command: "codex" }. - Update MCP / stdio integration tests that assert adapter list (add
codexto the expected set). - Update README and AGENTS.md adapter lists.
- Update
cuekit-adapter-run-modes-design.mdcommand matrix. - Update
cuekit-adapter-permission-bypass-design.md— codex inherits the same default-on bypass with-s <mode>as the opt-in restriction lever. - Update
packages/project-config/src/init.tsto render acodex:block in the generated config. - Add drift-prevention tests in
packages/project-config/__tests__/apply.test.tsmirroring the antigravity pattern. - Add
.cuekit.example.yamlblock foradapters.codex.permissions.
Adapter-level tests:
createCodexAdapter(...).capabilities()returnsagent_kind: "codex",supports_steering: true,supports_attach: true,supports_model_selection: true,default_mode: "interactive",supported_modes: ["interactive", "batch"].buildCodexLaunchCommand({ agent_kind: "codex", objective: "x" })defaults tocodex --dangerously-bypass-approvals-and-sandbox '<prompt>'.adapter_options.mode: "batch"prependsexecas a subcommand before flags.adapter_options.dangerously_skip_permissions: falseomits the bypass flag entirely (no replacement).adapter_options.sandbox: "workspace-write"emits-s workspace-writeand drops the bypass flag (sandbox wins).- All three sandbox modes (
read-only/workspace-write/danger-full-access) round-trip. - Invalid sandbox values silently fall back to the default bypass.
model: "gpt-5"emits-m 'gpt-5'.- Prompts containing single quotes are shell-quoted POSIX-style.
- Custom
codexBinpath is respected.
MCP-level tests:
list({ kind: "adapters" })includescodexand reportssupports_model_selection: true.- The stdio integration adapter count / expected agent kinds include
codex.
Project-config drift-prevention:
shouldForceSafeAdapterOptionshonorsadapters.codex.permissionsexactly (prompt forces, bypass does not).- A
codexconfig does NOT affect aclaude-codetask (verbatim agent_kind match).
Validation:
bun test packages/adapters
bun test packages/mcp
bun run typecheck
bunx biome check
bun testA real-runtime smoke test must cover:
- submit a task with
--agent_kind codex; tmux attach-session -t cuekit-task-<id>shows the live REPL;cuekit task steer ...reaches the running child;- transcript appears at
<cwd>/.cuekit/tasks/<task_id>/transcript.txt; cancel_taskscleans up the pane;- batch mode (
adapter_options: '{"mode":"batch"}') returnssupports_steering: falseintask status; - sandbox mode (
adapter_options: '{"sandbox":"workspace-write"}') launches with-s workspace-writeand without--dangerously-bypass-approvals-and-sandbox.
--dangerously-bypass-approvals-and-sandboxapproves every tool call AND removes the sandbox. The default-on choice mirrors the existing v0 behavior acrossclaude-code/antigravity. Project config keeps the right to forcedangerously_skip_permissions: falsefor prompt-safe defaults — the same override applies here unchanged.-s <mode>is the codex-native restriction lever.workspace-writeis the most common opt-in choice (write to repo, but no arbitrary filesystem / network);read-onlyis for pure inspection;danger-full-accessmatches the bypass-flag effect but goes through codex's sandbox machinery rather than skipping it entirely (subtly different audit story).- Invalid
adapter_options.sandboxvalues fall back to the default bypass silently. This matches the existing adapter_options handling (antigravity ignores non-truevalues for its booleansandbox). The trade-off is "fail soft" → the user gets default behavior rather than a hard error; the test pins this so the contract doesn't drift.
Resolved: implemented post-initial-ship. Dogfood surfaced a pane stall — submitting a task with-a, --ask-for-approvalpolicy.adapter_options.sandbox: "read-only"left codex's default approval policy active, and the unattended pane stalled on a "please approve" prompt. Fix: when sandbox is set the adapter now also emits-a <ask_for_approval ?? "never">so the pane doesn't stall; callers wanting human-in-the-loop pass an explicit policy.adapter_options.ask_for_approvalis also accepted without sandbox (drops the bypass flag, codex's default sandbox stays).-C, --cdand--add-dir. Multi-directory / explicit-cwd workflows. The pane backend already sets cwd; cuekit hasn't yet exposed multi-dir to children. Wire via adapter_options when needed.-c key=valueconfig overrides. Codex supports arbitrary TOML overrides at the CLI. Powerful but rarely needed for child-agent delegation. Defer.- Codex's own MCP server (
codex mcp-server). Codex can serve as an MCP server (stdio) for other agents. Not relevant to cuekit's child-agent adapter contract, but worth noting in the migration / interop guide.