Codex Adapter
Purpose: Current contract for the OpenAI Codex adapter: execution backends, hook translation, capability bounds, dependency policy, and the path to full interactive parity.
Read when: editing src/adapters/codex/, changing adapter manifests or hook rendering, or adding a programmatic Codex runtime.
Skip when: the change is Claude-only and does not alter a shared adapter port.
Nav: AGENTS.md > adapters > codex Status: live for Codex CLI/Desktop install, MCP, skills, hooks, formula dispatch, Hub observability, and read-only Hub transcripts; interactive start/send/cancel remains pending the shared runtime port.
Verified Baseline
The following was verified on 2026-08-03 against package registries, the installed binaries, official documentation, and executable interface probes.
| Surface | Repository state | Current upstream | Decision |
|---|---|---|---|
| Codex CLI | installed 0.146.0 |
stable 0.146.0 |
Primary production backend. No upgrade needed. |
| TypeScript Codex SDK | not a repo dependency | @openai/codex-sdk 0.146.0 |
Do not add a Node-to-Python bridge. coding-os already has an official Python SDK path. |
| Python Codex SDK | optional codex-sdk extra |
openai-codex 0.144.4 |
Official beta backend, selected explicitly; published builds include a pinned CLI runtime. |
| Generic Python OpenAI SDK | not a repo dependency | n/a | Do not add it for Codex dispatch. It is an API client, not the Codex thread runtime. |
| Claude Agent SDK | locked 0.2.110 |
comparison only | Claude dependency changes are independent of this Codex-only scope. |
The Python Codex SDK is official but still beta. Version 0.144.4 publishes with openai-codex-cli-bin==0.144.4. Its public CodexConfig, AsyncCodex.thread_start(), AsyncThread.run(), Sandbox, ApprovalMode, and TurnResult interfaces match the adapter's required one-turn contract. Therefore:
- Stable CLI execution is the default.
- The Python SDK may be installed through an optional extra and selected explicitly.
- SDK calls use the SDK's pinned runtime by default, as the official documentation recommends.
CodexConfig(codex_bin=...)is reserved for an intentional executable override, not normal dispatch. - Protocol validation failures must not silently fall back after a turn has started, because that could execute the same task twice.
runtime: in_processstays false until the shared Hub runtime port exists and the SDK model/session surface passes compatibility checks.
Hexagonal Boundary
The kernel owns DispatchRequest and DispatchResult. The Codex adapter owns every Codex-specific detail:
thinking_os DispatchRequest
|
v
src/adapters/codex/sdk_dispatcher.py
|
+-- default: codex exec --json, prompt over stdin
|
+-- optional: openai-codex AsyncCodex/app-server
|
v
thinking_os DispatchResult
The kernel must not import openai_codex, know Codex CLI flags, parse Codex JSONL, or translate Codex hook payloads. A second adapter should require a manifest, installer, optional dispatcher, and tests, not provider conditionals in src/core/.
The remaining exception is writable Hub chat: src/core/web/routes/cognition.py still owns Claude-only start/send behavior. Transcript discovery and reading are adapter-loaded through the manifest chat_provider port, so core does not import openai_codex. Making Codex writable still requires extracting start/resume/send/cancel into the shared runtime port and migrating Claude behind it.
Dispatcher Contract
The stable CLI backend runs one ephemeral, read-only formula turn:
codex --ask-for-approval never exec --ignore-user-config --disable hooks --config 'mcp_servers={}' --json --ephemeral --sandbox read-only -
request.model adds --model <id>. The composed prompt is written to stdin, avoiding command-line length limits and process-list exposure. The adapter parses JSONL and takes the last completed agent_message as the final response. It then extracts the EvidenceBundle JSON block into DispatchResult.output_json.
User configuration is ignored and formula hooks plus MCP servers are disabled for the sub-run. This prevents recursive lifecycle hooks and removes external mutation paths that a read-only filesystem sandbox cannot constrain. Roles that opt into structured output pass their Pydantic schema through CLI --output-schema or SDK output_schema.
The former invocation, codex --no-interactive --json <prompt>, is not valid on current Codex. Non-interactive execution is the exec subcommand, and --no-interactive is not a current flag.
The optional Python SDK backend uses AsyncCodex with:
- the published SDK's pinned Codex runtime;
CodexConfig.config_overridesto disable hooks and MCP servers;Sandbox.read_onlyandApprovalMode.deny_allfor formula output;developer_instructionsfor the formula body;thread.run()for one turn;- an outer timeout owned by the adapter.
No Codex backend currently enforces max_budget_usd. A request that supplies it fails before dispatch; silently dropping a financial ceiling violates the dispatcher contract.
Capability Matrix
Parity means equivalent kernel outcomes, not identical provider APIs.
| Capability | Claude adapter | Codex adapter target | State |
|---|---|---|---|
| Durable repository guidance | CLAUDE.md/AGENTS.md |
AGENTS.md hierarchy |
native |
| Reusable skills | Claude skills | Codex agent skills | native |
| Slash-command Markdown | .claude/commands |
Codex custom prompts are deprecated; skills are the supported replacement | degraded, migrate to skills |
| MCP client | stdio/HTTP through Claude config | stdio/HTTP through .codex/config.toml |
native |
| Read-tool policy hooks | native Read matcher |
native local-function Read matcher |
native and rendered |
| Formula dispatch | Claude Agent SDK | stable CLI plus optional Python SDK | implemented by adapter |
| Structured output | SDK output format | CLI/SDK strict schema when compatible; JSON-block extraction otherwise | native with deterministic fallback |
| Session continuation | SDK resume | CLI exec resume / SDK thread resume |
native, not required by one-turn formula dispatch |
| Sandbox | SDK permissions and tools | read-only, workspace-write, danger-full-access |
native |
| Per-tool allowlist | SDK allowed_tools/disallowed_tools |
no equivalent Python SDK argument | degraded; formula runs narrow to read-only with MCP disabled |
| Pre/Post Bash hooks | native | native | parity |
| Pre/Post file-edit hooks | Write/Edit payload |
apply_patch, alias-matched as Edit, patch in tool_input.command |
adapter translation implemented |
| MCP tool hooks | native matchers | native MCP tool-name matchers | parity |
| Prompt context injection | UserPromptSubmit output |
UserPromptSubmit.additionalContext |
parity |
| Compact lifecycle | SessionStart compact plus SDK hooks | SessionStart compact, PreCompact, PostCompact | Codex has the required events |
| Subagent lifecycle | start/stop | start/stop | parity for shared events |
| Tool-failure hook | PostToolUseFailure |
no matching Codex event | impossible today; observe error items instead |
| Permission hook | SDK/CLI permission callbacks | PermissionRequest |
native, output schemas differ |
| Hook trust | project/settings trust | hash-based review for non-managed hooks | explicit operator step required |
| Hub sessions, board, logs, traces | native Claude identity | native Codex identity | parity through adapter-owned environment and ses-codex-* ids |
| Hub transcript list/read | Claude Agent SDK | official Codex Python SDK thread/list + thread/read |
native, adapter-loaded |
| Hub start/send/cancel | Claude Agent SDK | official Codex Python SDK/app-server | requires shared writable runtime port |
Runtime Identity Contract
Codex Desktop does not guarantee CODEX_SESSION_ID, CODEX_AGENT_DIR, or CODEX_HOME in every hook and shell subprocess. Adapter identity must therefore be established by the adapter, not inferred from .coding-os/.agent or from the selected model name.
The Codex install has three required identity legs:
- Every rendered hook command starts with
COS_AGENT=codex; adapter-private dispatchers repeat that assertion before sourcingcos-env.shso direct invocation is safe too. - Project-local
.codex/config.tomlsetsCOS_AGENT,COS_STATE_DIR, andCOS_AGENT_DIRfor Codex shell subprocesses and for thecoding-osMCP server. - Each hook payload's
session_idupgrades the panel identity, producingses-codex-*session ids and state under.coding-os/codex/.
.coding-os/.agent is only a legacy/plain-shell fallback. It is intentionally not the source of truth for a project that installs both Claude and Codex, because one scalar cannot identify two concurrent runtimes. .coding-os.yaml::agents is the installed-adapter SSOT; adapter-owned runtime environment is the calling-agent SSOT.
Hub does not guess an agent from model. It reads the agent-owned presence directory and payload, while board and work-log attribution follow agent_session. A GPT model appearing under claude is therefore evidence of an upstream identity failure, not a UI-label bug.
2026-08 Refresh Checklist
- Read the current official Codex SDK and hooks references.
- Compare installed CLI, published TypeScript SDK, published Python SDK, lockfile, and adapter dependency floors.
- Compare Claude and Codex manifests, installers, dispatcher contracts, hook coverage, and executable tests.
- Upgrade the optional Python SDK and its bundled runtime floor to the current compatible release.
- Make dispatcher availability backend-aware and let the Python SDK use its officially pinned runtime without requiring a global
codexbinary. - Normalize strict-compatible Pydantic schemas and reject incompatible arbitrary-map/default schemas before the provider rejects the turn.
- Render supported Codex
Readhooks so graph-first and task-discovery policy is not silently weaker than Claude. - Regenerate derived templates and Codex golden fixtures; review every generated diff.
- Run targeted dispatcher, adapter, hook, CLI, docs, and real read-only SDK smoke verification.
- Run graph change detection and final diff review before closing the task.
- Prove a fresh Codex CLI/Desktop-compatible session writes
.coding-os/codex, usesses-codex-*, and renders as Codex in Hub sessions, board, work logs, and traces. - Prove Codex project shell and MCP subprocesses retain Codex identity when all optional
CODEX_*markers are absent and.coding-os/.agentnames Claude.
The checklist deliberately leaves unsupported controls visible rather than emulating them unsafely: Codex still has no per-turn USD ceiling, no independent PostToolUseFailure event, and no adapter-local path to the Claude-coupled Hub chat route. OpenAI Structured Outputs also requires every object to set additionalProperties: false and every property to be required. Role schemas with arbitrary dictionaries or defaulted fields therefore use the existing JSON-block path instead of starting a turn that the provider will reject.
Hook Translation
Codex PreToolUse and PostToolUse can intercept Bash, MCP calls, and file edits performed through apply_patch. For file edits, Codex keeps tool_name: "apply_patch" and puts the patch in tool_input.command; Claude-oriented core hooks expect tool_name: "Edit" and tool_input.file_path.
The translation belongs at the adapter boundary:
- The manifest uses
Editas Codex's canonical alias so all registry matchers (Write|Edit,Write|Edit|MultiEdit, andEdit) collapse into one deterministic group. - An adapter-private normalizer extracts every
Add File,Update File,Delete File, andMove topath from the patch. - The edit dispatcher invokes the unchanged core hooks once per affected path with a Claude-shaped payload.
- PreToolUse exits
2immediately when a delegate blocks. - PostToolUse is advisory because the edit has already happened.
This avoids leaking Codex patch grammar into src/core/hooks/** and keeps the kernel agent-agnostic. The cost is proportional to files x hooks; typical patches are small, while correctness requires checking every affected path.
Codex runs multiple matching command hooks concurrently. Dispatcher groups therefore preserve the registry's safety-to-observability ordering. A dispatcher must also forward delegate additionalContext; dropping stdout makes prompt, Stop, and reminder hooks silent even though the runtime supports them.
Codex also emits SessionEnd. Coding OS uses an adapter dispatcher that first
upgrades panel identity from the event payload and then writes the final
presence transition to ended. Directly invoking the shared presence hook
without that upgrade can target the wrong panel or no session at all, leaving
a dead process rendered as an active Codex chat. Per-turn recap and learning
remain on Stop, because Stop and SessionEnd have different lifecycle
meanings.
Hook Trust
Non-managed Codex command hooks are trusted by exact hash. New or changed hooks are skipped until reviewed with /hooks. The installer must:
- enable the canonical
hooksfeature; - remove obsolete project feature keys that current Codex rejects, including
codex_hooksandrmcp_client; - install and render the project hook configuration;
- print a clear
/hooksreview step; - never mutate Codex's private trust store or use
--dangerously-bypass-hook-trustas a persistent default.
Managed enterprise deployments may enforce trusted hooks through requirements.toml; that is an administrator policy surface, not a project installer shortcut.
Adapter Complexity
These are engineering estimates derived from this repository's contracts, not provider promises.
| Scope | Typical effort | Main risk |
|---|---|---|
| Manifest + idempotent installer + MCP wiring | 0.5-1 day | filesystem/config conventions |
| Skills/rules/instructions mapping | 1-2 days | provider UX differences and deprecated surfaces |
| Hook enforcement parity | 2-4 days | event coverage, payload translation, ordering, trust |
| One-turn formula dispatcher | 1-2 days | timeout, auth, sandbox, stream parsing, structured result |
| Interactive Hub runtime | 4-8 days after a shared port exists | resume, streaming, cancellation, tools, presence, models |
| New provider with weak hooks or no SDK | 1-3 weeks | honest degraded modes and executable end-to-end evidence |
Adding a directory is easy. Shipping an adapter that preserves safety, lifecycle, observability, and UX is not. The architecture should make provider differences declarative or adapter-local; it cannot manufacture lifecycle events that a provider does not expose.
Delivery Plan
Phase A - Current Adapter Closure (completed 2026-07-10)
- Correct the stable CLI dispatcher and JSONL parsing.
- Add the latest Python SDK as an optional beta extra, with explicit backend selection and active-binary override.
- Update Codex hook capabilities to current official events and MCP/edit matchers.
- Add adapter-private
apply_patchnormalization and deterministic edit dispatchers. - Forward
additionalContextfrom prompt, Stop, pre-tool, and post-tool dispatcher groups. - Regenerate templates and golden fixtures; run adapter, hook, dispatcher, and real read-only smoke tests.
Phase A2 - Current SDK Refresh (2026-08-03)
- Replace the obsolete
0.1.0b3Python SDK line with the current compatible0.144.4line. - Stop overriding the SDK's bundled runtime during normal Python dispatch.
- Make CLI and Python SDK availability independent while preserving CLI as the default.
- Validate Codex strict-output compatibility before forwarding a role schema.
- Close verified
Readhook coverage and regenerate adapter artifacts.
Phase B - Shared Interactive Runtime Port
Complete the adapter-loaded protocol from Hub cognition with operations for:
- availability and capability discovery;
- start/resume/send/cancel;
- normalized stream events (
text,tool_start,tool_end,usage,error,done); - model and reasoning-effort discovery;
- session identity and presence lookup (transcript list/read is implemented);
- sandbox, approval, tool, and budget capabilities.
Migrate Claude behind that port without changing behavior, then add Codex. This phase changes the Claude path and requires its own migration task and rollback evidence.
Phase C - Command UX Consolidation
Move reusable command semantics into skills, keep provider-specific slash commands as thin aliases where supported, and stop treating .codex/commands as a native Codex surface. Evaluate a coding-os Codex plugin only if it removes installer/config duplication without weakening project-local versioning.
Phase D - Cross-Adapter Orchestration
After both runtimes implement the shared port, let the supervisor select an adapter from declared capabilities, model policy, budget, and task shape. Adapter choice must remain observable and overridable; no hidden provider switch is allowed.
Sources
- Codex SDK
- Codex hooks
- Codex advanced config and hooks
- Codex config reference
- Codex AGENTS.md
- Codex CLI reference
- Codex MCP
- Codex subagents
- Codex skills
- Codex custom prompts
- Codex TypeScript SDK source
- Codex Python SDK source
- Claude Agent SDK hooks