-
-
Notifications
You must be signed in to change notification settings - Fork 33
Integration Codex
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.
| 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. |
-
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.3added it toAGENT_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 stampsmetadata.host='codex'on the bridge itself + filter recognizesmetadata.bridge===true. -
0.1.5(2026-05-15) — strict ownership: install-time roster only includes accounts withmetadata.host === 'codex'. Unclaimed accounts no longer auto-inherited.
- The dispatcher's
shouldWatchstill falls through to "watch" on unclaimed accounts (nometadata.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-sdkis a peer dep (not a hard dep) — the installer requires the user to havenpm install -g @openai/codexseparately. We considered bundling the SDK but the version coupling is tighter than we want. - No
@agenticmail/host-toolkitshared library yet. The dispatcher / catch-up / state-persistence code is duplicated between@agenticmail/claudecodeand@agenticmail/codex. Worth the duplication for two hosts; will be refactored when a third host joins.
(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.
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.
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.rsreadsparsed.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) ANDstreamable_http(url,bearer_token_env_var,http_headers) transports, plusenabled,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/sdkas 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 = trueCodex has a real multi-agent system, not just session import.
- Model-facing tool:
spawn_agent(v1 + v2 variants incodex-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'sAgenttool exposes. - Schema includes
agent_type(string — thesubagent_typeequivalent),message,model,reasoning_effort,service_tier,fork_turns,fork_context. - Definitions live at
$CODEX_HOME/agents/<name>.tomlor<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.enabledinconfig.toml. Default ismulti_agent_is_stable_and_enabled_by_default = truebut a one-time TUI confirmation prompt exists. Our installer should set this explicitly to skip the prompt.
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.
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).
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, customenv, andconfig: { ... }overrides (serialized as repeated--config key=value). -
Mechanically: the SDK spawns the
codexCLI 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 execmode (docs/exec.md) for shell-level invocation, and a Python SDK atsdk/python/.
- Primary:
npm install -g @openai/codex(codex-cli/package.json→"bin": { "codex": "bin/codex.js" }). - Config home:
~/.codex/(overridable viaCODEX_HOMEenv 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.
The shape of @agenticmail/codex:
-
Installer (
@agenticmail/codex, npm-global). Onpostinstall:- Resolve
CODEX_HOME(env or~/.codex). - Merge
[mcp_servers.agenticmail]into~/.codex/config.toml(stdio transport,command = "agenticmail-mcp"). Use@iarna/toml(read) +toml-editsemantics (preserve formatting) — straightforward. - Ensure
features.multi_agent_v2.enabled = truesospawn_agentis 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.jsonforSessionStart(capabilities blurb + account context),UserPromptSubmit(mail-backlog digest), andStop(autonomous-mode mail awareness). Hook scripts ported fromclaudecode/src/mail-hook.tswith only the config-file paths changed.
- Resolve
-
Dispatcher daemon — same architecture as
dispatcher.tsbut swap@anthropic-ai/claude-agent-sdk'squery()fornew Codex().startThread().run(prompt). UseresumeThread(threadId)for per-account session continuity (Codex persists threads to~/.codex/sessionsfor free — even better than what we have today). -
Model dispatch path — preserved. The parent agent calls
spawn_agent({ agent_type: "vesper", message: "..." })instead of Claude'sAgent({ 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. -
No SDK polyfill needed —
@openai/codex-sdkis the canonical replacement for@anthropic-ai/claude-agent-sdk. Both wrap their CLI, both speak JSONL over stdio, both expose the samestart → run → resumelifecycle.
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 |
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.tssurvives unchanged - mail-hook port: <1 day — just path changes + the new
~/.codex/hooks.jsonshape - Tests + parity validation: 2 days
-
Total: ~1 week for a buildable, shippable
@agenticmail/codex@0.1.0.
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.
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.rs—spawn_agentand 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:3564—CODEX_HOMEresolution