Skip to content

Integration Codex

Ope Olatunji edited this page May 15, 2026 · 3 revisions

Integration: Codex CLI (OpenAI)

Status: Shipping as @agenticmail/codex (latest 0.1.5, published 2026-05-15). The plan documented below is what was implemented; this page now records both the plan and the actual implementation details.

Implementation status (2026-05-15)

Surface Shipped Notes
Installer agenticmail-codex install. Writes ~/.codex/config.toml ([mcp_servers.agenticmail] + [features.multi_agent_v2]), ~/.codex/hooks.json, and one .toml per owned AgenticMail account in ~/.codex/agents/.
Uninstaller agenticmail-codex uninstall [--purge-bridge]. Removes the MCP block + hook entries + owned subagent files; optionally deletes the codex bridge account.
Status agenticmail-codex status [--json]. Reports state (installed / partial / not_installed), bridge existence, MCP registration, dispatcher PM2 status.
Tuning agenticmail-codex tune [--max-concurrent N --max-wakes-per-thread N --wake-window-ms N --wake-coalesce-ms N --sync-ms N --reset --json]. Writes ~/.agenticmail/dispatcher.json.
Claim CLI agenticmail-codex claim <name> [--all --unclaim --json]. Sets metadata.host = 'codex' on the named accounts so the codex dispatcher (and only the codex dispatcher) watches them.
Hook engine compatibility ✅ confirmed Codex's ClaudeHooksEngine Rust crate accepts the Claude Code hook JSON shape verbatim. The same mail-hook.js binary registers under ~/.codex/hooks.json and ~/.claude/settings.json with no changes to the script.
Dispatcher agenticmail-codex-dispatcher PM2 daemon. Uses @openai/codex-sdk's new Codex().startThread().run(prompt) where the Claude Code dispatcher uses claude-agent-sdk's query(). Same SSE channel-per-account architecture, same per-agent serialization, same wake-budget, same catch-up scan on restart.
Subagent file format ~/.codex/agents/agenticmail-<name>.toml with name, description, developer_instructions, model. developer_instructions carries the same persona body the Claude Code package generates, transformed from markdown-with-YAML-frontmatter to TOML-with-heredoc once at install time.
MCP env-var auto-stamp Installer writes AGENTICMAIL_MCP_HOST=codex into the MCP server's env block in config.toml. Every account created from a Codex session via create_account gets metadata.host='codex' automatically.
Bridge host stamp ✅ (since 0.1.5) The codex bridge account is itself stamped metadata.host='codex' by the installer (it's created via the master API, not MCP, so the env-var path doesn't apply).
Strict subagent ownership ✅ (since 0.1.5) selectExposableAgents exposes ONLY accounts where metadata.host === 'codex'. A fresh Codex install on a machine with Claude Code already set up writes ZERO teammate subagent files — Codex starts with an empty roster and builds its own.
@agenticmail/host-toolkit shared library ❌ not yet Both host packages still have their own copies of the dispatcher core. Refactor planned once Grok Build joins.

Versions shipped

  • 0.1.0 (2026-05-14) — initial shipping release. Workaround: bridge role was 'assistant' because the API hadn't accepted 'bridge' yet.
  • 0.1.1 — codex install role-bug fix (kept the 'assistant' workaround explicit).
  • 0.1.2 (2026-05-15) — reverted bridge role to 'bridge' once @agenticmail/core 0.9.3 added it to AGENT_ROLES. Added auto-migration of legacy 'assistant' bridges.
  • 0.1.3 (2026-05-15) — cross-host dispatcher filter, host-ownership awareness.
  • 0.1.4 (2026-05-15) — installer stamps metadata.host='codex' on the bridge itself + filter recognizes metadata.bridge===true.
  • 0.1.5 (2026-05-15) — strict ownership: install-time roster only includes accounts with metadata.host === 'codex'. Unclaimed accounts no longer auto-inherited.

Known limitations carried into the next milestone

  • The dispatcher's shouldWatch still falls through to "watch" on unclaimed accounts (no metadata.host), matching the Claude Code dispatcher's back-compat behavior. The install-time filter is strict; dispatcher-time isn't. Plan: tighten in 0.10 after telemetry shows the MCP env-var auto-stamp is universally present.
  • @openai/codex-sdk is a peer dep (not a hard dep) — the installer requires the user to have npm install -g @openai/codex separately. We considered bundling the SDK but the version coupling is tighter than we want.
  • No @agenticmail/host-toolkit shared library yet. The dispatcher / catch-up / state-persistence code is duplicated between @agenticmail/claudecode and @agenticmail/codex. Worth the duplication for two hosts; will be refactored when a third host joins.

Original research notes — kept for context

(Below this point is the original pre-shipping design doc.) ~90% architectural overlap with the Claude Code integration — the highest of the three planned hosts.

Reality check

Codex CLI is OpenAI's first-party agentic coding tool. Released as openai/codex on GitHub, currently on release line 0.131.0-alpha.* (May 2026). Apache-2.0. Distributed via:

  • npm install -g @openai/codex (primary)
  • brew install --cask codex
  • Platform tarballs from GitHub Releases

Findings below come from reading the openai/codex repo at HEAD — the file paths are the authoritative source, not blog posts.

Most surprising finding: Codex CLI's hook engine is literally named ClaudeHooksEngine in the Rust source (codex-rs/hooks/). The event names, JSON I/O schema, and config file shape are byte-for-byte compatible with Claude Code's hook contract. OpenAI explicitly adopted Anthropic's hook ABI.

1. MCP server registration — first-class

Codex is built around MCP. Servers are declared in ~/.codex/config.toml (override with CODEX_HOME) under an [mcp_servers] TOML table.

  • Loader: codex-rs/config/src/mcp_edit.rs reads parsed.get("mcp_servers").
  • Programmatic write helpers (load_global_mcp_servers, RawMcpServerConfig) are part of the public Rust config crate.
  • Each entry supports BOTH stdio (command, args, env, env_vars, cwd) AND streamable_http (url, bearer_token_env_var, http_headers) transports, plus enabled, required, startup_timeout_sec, tool_timeout_sec, default_tools_approval_mode, supports_parallel_tool_calls.
  • An RMCP client lives at codex-rs/rmcp-client/. The TS SDK declares @modelcontextprotocol/sdk as a dep.

Integration shape: write a TOML block under [mcp_servers.agenticmail] in ~/.codex/config.toml. Direct analog of the Claude Code mcpServers write, just TOML instead of JSON.

[mcp_servers.agenticmail]
command = "agenticmail-mcp"
args = []
enabled = true

2. Sub-agents as native callable agents — first-class (feature-gated)

Codex has a real multi-agent system, not just session import.

  • Model-facing tool: spawn_agent (v1 + v2 variants in codex-rs/core/src/tools/handlers/multi_agents_spec.rs). Companion tools: send_input, send_message, followup_task, wait_agent, list_agents, close_agent, resume_agent — a richer cross-agent messaging surface than Claude Code's Agent tool exposes.
  • Schema includes agent_type (string — the subagent_type equivalent), message, model, reasoning_effort, service_tier, fork_turns, fork_context.
  • Definitions live at $CODEX_HOME/agents/<name>.toml or <repo>/.codex/agents/<name>.toml. Discovery: codex-rs/core/src/config/agent_roles.rs::discover_agent_roles_in_dir. Required fields: name, description, developer_instructions. Optional: model, model_reasoning_effort, nickname candidates.
  • Sample agent: codex-rs/core/src/agent/builtins/awaiter.toml.
  • Gating: features.multi_agent_v2.enabled in config.toml. Default is multi_agent_is_stable_and_enabled_by_default = true but a one-time TUI confirmation prompt exists. Our installer should set this explicitly to skip the prompt.

Translation from Claude Code subagent files

Claude Code: ~/.claude/agents/agenticmail-vesper.md with YAML frontmatter + markdown body.

Codex:

# ~/.codex/agents/agenticmail-vesper.toml
name = "agenticmail-vesper"
description = "Vesper — AgenticMail account with persistent inbox + the full toolbelt"
model = "gpt-5"
developer_instructions = """
[the same persona body we already generate for Claude Code,
verbatim — it's just markdown text either way]
"""

One small transform step at install time, then the same persona content drives both hosts.

3. Lifecycle hooks — byte-for-byte identical to Claude Code

The Codex hooks crate is named ClaudeHooksEngine and emits the same event set with the same JSON contracts. This is the most consequential finding for porting effort.

Events (from codex-rs/hooks/src/lib.rs:18):

pub const HOOK_EVENT_NAMES: [&str; 8] = [
    "PreToolUse",
    "PermissionRequest",     // ← only Codex
    "PostToolUse",
    "PreCompact",
    "PostCompact",
    "SessionStart",
    "UserPromptSubmit",
    "Stop",
];

Codex has one extra event (PermissionRequest) but the seven shared events match Claude Code exactly.

Wire schemas are generated and live at codex-rs/hooks/schema/generated/*.schema.json. Input fields: hook_event_name, session_id, cwd, model, permission_mode, transcript_path. Output supports continue, decision: "block", reason, stopReason, systemMessage, suppressOutput, and per-event hookSpecificOutput (e.g. UserPromptSubmitHookSpecificOutput.additionalContext).

Config file is <config_folder>/hooks.json (separate file from config.toml). Layers searched (codex-rs/hooks/src/engine/discovery.rs:289): $CODEX_HOME/hooks.json (user), <repo>/.codex/hooks.json (project), system, MDM, plus plugin hooks/hooks.json. Hooks can also be declared in TOML under [hooks] in config.toml.

Schema (verbatim from codex-rs/hooks/src/engine/mod_tests.rs:828):

{
  "hooks": {
    "UserPromptSubmit": [
      { "matcher": "", "hooks": [{ "type": "command", "command": "node /path/to/mail-hook.js" }] }
    ],
    "SessionStart": [...],
    "Stop": [...]
  }
}

Practical impact for AgenticMail: the existing mail-hook.ts runs unchanged. Only the registration file changes: write to ~/.codex/hooks.json instead of ~/.claude/settings.json. Note: Claude Code's hooks block lives INSIDE settings.json; Codex puts hooks in their own hooks.json (cleaner separation, easier to merge idempotently).

4. Headless / programmatic mode — official Node SDK

The most welcome finding: there's an @openai/codex-sdk npm package that's effectively @anthropic-ai/claude-agent-sdk's OpenAI twin.

  • Package: @openai/codex-sdk (sdk/typescript/package.json), ESM, Node 18+.

  • Hard dep on @modelcontextprotocol/sdk@^1.24.0.

  • API:

    import { Codex } from '@openai/codex-sdk';
    const codex = new Codex();
    const thread = codex.startThread();
    const result = await thread.run("Read the latest mail and reply.");
    // result: { finalResponse, items, ... }
    
    // OR streaming:
    for await (const event of thread.runStreamed("...")) {
      // event: ItemStarted | ItemUpdated | ItemCompleted | TurnCompleted
    }
    
    // Resume:
    const resumed = codex.resumeThread(threadId);
  • resumeThread(id) rehydrates from ~/.codex/sessions — built-in persistence.

  • Supports outputSchema, attached images, skipGitRepoCheck, custom env, and config: { ... } overrides (serialized as repeated --config key=value).

  • Mechanically: the SDK spawns the codex CLI and exchanges JSONL on stdio. A long-running dispatcher daemon can reuse one Codex client to spawn many one-shot turns the same way it does today with @anthropic-ai/claude-agent-sdk.

  • Also exposed: a non-interactive codex exec mode (docs/exec.md) for shell-level invocation, and a Python SDK at sdk/python/.

5. Installation pattern — npm-global

  • Primary: npm install -g @openai/codex (codex-cli/package.json"bin": { "codex": "bin/codex.js" }).
  • Config home: ~/.codex/ (overridable via CODEX_HOME env var — codex-rs/core/src/config/mod.rs:3564).
  • Subdirs we'd touch:
    • ~/.codex/config.toml — MCP servers, feature flags
    • ~/.codex/hooks.json — lifecycle hooks
    • ~/.codex/agents/*.toml — subagent definitions
    • ~/.codex/skills/ — skills cache
    • ~/.codex/sessions/ — thread rollouts
    • ~/.codex/log/ — local logs
  • Project-scoped overlays: <repo>/.codex/agents/*.toml, <repo>/.codex/hooks.json.

The integration package mirrors the Claude Code installer one-to-one: write TOML/JSON files into $CODEX_HOME on postinstall.

Verdict — feasible, first-class on all five axes. Highest reuse of the three planned hosts.

The shape of @agenticmail/codex:

  1. Installer (@agenticmail/codex, npm-global). On postinstall:

    • Resolve CODEX_HOME (env or ~/.codex).
    • Merge [mcp_servers.agenticmail] into ~/.codex/config.toml (stdio transport, command = "agenticmail-mcp"). Use @iarna/toml (read) + toml-edit semantics (preserve formatting) — straightforward.
    • Ensure features.multi_agent_v2.enabled = true so spawn_agent is exposed.
    • Write one TOML file per account into ~/.codex/agents/: name, description, developer_instructions (same persona body we already generate for Claude Code, just transformed from markdown-with-frontmatter to TOML-with-heredoc).
    • Merge AgenticMail hook entries into ~/.codex/hooks.json for SessionStart (capabilities blurb + account context), UserPromptSubmit (mail-backlog digest), and Stop (autonomous-mode mail awareness). Hook scripts ported from claudecode/src/mail-hook.ts with only the config-file paths changed.
  2. Dispatcher daemon — same architecture as dispatcher.ts but swap @anthropic-ai/claude-agent-sdk's query() for new Codex().startThread().run(prompt). Use resumeThread(threadId) for per-account session continuity (Codex persists threads to ~/.codex/sessions for free — even better than what we have today).

  3. Model dispatch path — preserved. The parent agent calls spawn_agent({ agent_type: "vesper", message: "..." }) instead of Claude's Agent({ subagent_type: "vesper", ... }). Codex's richer subagent messaging tools (send_message, followup_task, wait_agent, list_agents) could eventually let us surface live cross-agent messaging more cleanly than today — but that's a v2 enhancement, not v1.

  4. No SDK polyfill needed@openai/codex-sdk is the canonical replacement for @anthropic-ai/claude-agent-sdk. Both wrap their CLI, both speak JSONL over stdio, both expose the same start → run → resume lifecycle.

Real differences vs the Claude Code integration

None are blockers. Each is a one-time transform during install:

Aspect Claude Code Codex
Config format JSON (~/.claude.json) TOML (~/.codex/config.toml)
Hooks location nested in ~/.claude/settings.json separate file ~/.codex/hooks.json
Agent definitions markdown + YAML frontmatter TOML with developer_instructions = """..."""
Spawn tool name Agent spawn_agent
Spawn arg name subagent_type agent_type
Feature gate none features.multi_agent_v2.enabled = true
SDK @anthropic-ai/claude-agent-sdk @openai/codex-sdk

Estimated effort

This integration is the easiest of the three. Rough breakdown:

  • Installer (TOML/JSON writers + persona transformer): 1-2 days
  • Dispatcher port (swap SDK calls): 1 day — most of dispatcher.ts survives unchanged
  • mail-hook port: <1 day — just path changes + the new ~/.codex/hooks.json shape
  • Tests + parity validation: 2 days
  • Total: ~1 week for a buildable, shippable @agenticmail/codex@0.1.0.

Open question for v2 (not blocking v1)

Codex's send_message / wait_agent / list_agents tools let agents talk to each other ENTIRELY INSIDE the Codex runtime, bypassing email. This is conceptually overlapping with AgenticMail's "agents email each other" model. Two valid framings for v2:

  • (a) Treat Codex's intra-runtime messaging as the fast path for same-host coordination; AgenticMail handles cross-host / cross-runtime / durable threads.
  • (b) Ignore Codex's intra-runtime messaging entirely; every cross-agent message still goes through AgenticMail's mail layer so the audit trail is consistent across hosts.

(b) is the easier v1 stance. (a) is a possible v2 optimization once we have telemetry on how often agents on the same host talk to each other.

Source paths

All authoritative, in the openai/codex repo at HEAD:

  • codex-rs/hooks/ — hook engine + JSON schemas (ClaudeHooksEngine)
  • codex-rs/config/src/mcp_edit.rs — MCP server config read/write
  • codex-rs/core/src/tools/handlers/multi_agents_spec.rsspawn_agent and friends
  • codex-rs/core/src/config/agent_roles.rs — agent TOML discovery
  • codex-rs/core/src/agent/builtins/awaiter.toml — example agent definition
  • sdk/typescript/README.md — Node SDK contract
  • codex-cli/package.json — npm-global CLI package
  • codex-rs/core/src/config/mod.rs:3564CODEX_HOME resolution

Clone this wiki locally