Codex Rust Wrapper
Async wrapper around the Codex CLI focused on the headless codex exec flow. The client shells out to the bundled or system Codex binary, mirrors stdout/stderr when asked, and keeps the parent process environment untouched.
- crates.io package:
unified-agent-api-codex - Rust library crate:
codex
Capability + versioning release notes (Workstream F)
- Capability probes now capture
codex --version,codex features list(--jsonwhen available), and--helphints, storing results asCodexCapabilitiessnapshots withcollected_attimestamps andBinaryFingerprintmetadata keyed by canonical binary path. - Guard helpers (
guard_output_schema,guard_add_dir,guard_mcp_login,guard_features_list) keep optional flags off when support is unknown; surfaceCapabilityGuard.notesto operators instead of passing flags blindly. - Cache controls: configure
CapabilityCachePolicy::{PreferCache, Refresh, Bypass}viacapability_cache_policyorbypass_capability_cache. UseRefreshfor TTL/backoff windows or hot-swaps that reuse the same path; useBypasswhen metadata is missing (FUSE/overlay filesystems) or when you need an isolated probe that skips cache reads/writes. - TTL/backoff helper:
capability_cache_ttl_decisioninspectscollected_atand fingerprint presence to recommendRefreshvsBypassfor hot-swaps or metadata-missing paths (FUSE/overlay); start with a ~5 minute TTL and back off toward 10-15 minutes when metadata keeps failing. - Overrides + persistence:
capability_snapshot/capability_overridesaccept manual snapshots and feature/version hints;write_capabilities_snapshot,read_capabilities_snapshot, andcapability_snapshot_matches_binarylet hosts reuse snapshots across processes while avoiding stale data when fingerprints diverge. - Update advisories stay offline: supply
CodexLatestReleasesand callupdate_advisory_from_capabilitiesto prompt upgrades without this crate performing network I/O.
Snapshot reuse + cache policy quickstart
Run the snapshot example to see disk reuse with fingerprint checks plus cache policy guidance:
cargo run -p unified-agent-api-codex --example capability_snapshot -- ./codex ./codex-capabilities.json auto
- The example loads a prior snapshot when the fingerprint matches, falls back to
CapabilityCachePolicy::Refreshafter a TTL or hot-swap, and drops toCapabilityCachePolicy::Bypasswhen metadata is missing (typical on some FUSE/overlay mounts) to avoid persisting snapshots that cannot be validated. - Refresh vs. Bypass: use
Refreshto re-probe while still writing back to the cache (good for TTL/backoff windows or deployments that reuse the same path); useBypassfor one-off probes that should not read or write cache entries when metadata is unreliable.
See crates/codex/examples/capability_snapshot.rs for the full flow, including fingerprint validation and snapshot persistence helpers.
TTL/backoff helper
Use capability_cache_ttl_decision to decide whether to reuse a cached snapshot or force a probe with the right cache policy:
use ;
use ;
async
Refreshcovers hot-swaps that reuse the same binary path even when fingerprints look unchanged.Bypassis returned when metadata is missing; avoid cache writes and increase the TTL/backoff window to reduce probe churn.
Binary and CODEX_HOME isolation
- Point the wrapper at a bundled Codex binary via [
CodexClientBuilder::binary]; if unset, it honorsCODEX_BINARYor falls back tocodexonPATH. - Apply an app-scoped home with [
CodexClientBuilder::codex_home]. The resolved binary is mirrored intoCODEX_BINARY, and the provided home is exported asCODEX_HOMEfor every spawn site (exec/login/status/logout). The parent environment is never mutated. - Use [
CodexClientBuilder::create_home_dirs] to control whetherCODEX_HOME,conversations/, andlogs/are created up front (defaults totruewhen a home is set).RUST_LOGdefaults toerrorif you have not set it.
use ;
async
CODEX_HOME layout helper
CodexHomeLayout documents where Codex stores state under an app-scoped home:
config.tomlauth.json.credentials.jsonhistory.jsonlconversations/for transcript JSONL fileslogs/forcodex-*.logfiles
Call [CodexHomeLayout::materialize] to create the root, conversations/, and logs/ directories before spawning Codex.
Stream JSONL events
Use the streaming surface to consume codex exec --json output as it arrives. Disable stdout mirroring so you control the console, and set an idle timeout to fail fast if the CLI stalls.
use ;
use StreamExt;
use ;
# async
Log the raw JSON stream
Set json_event_log on the builder or per request to tee every raw JSONL line to disk before parsing:
- The log is appended to (existing files are preserved) and flushed per line.
- Parent directories are created automatically.
- An empty string is ignored; set a real path or leave
Noneto disable. - The per-request
json_event_logoverrides the builder default for that run.
Events still flow to your events stream even when teeing is enabled.
Apply or inspect task diffs
CodexClient::apply_task wraps codex apply <TASK_ID>, and CodexClient::cloud_diff_task wraps codex cloud diff <TASK_ID> when supported by the binary. CodexClient::apply/CodexClient::diff are convenience helpers that will append <TASK_ID> from CODEX_TASK_ID when set.
All of these capture stdout/stderr and return the exit status via ApplyDiffArtifacts. They honor the builder flags you already use for streaming:
mirror_stdoutcontrols whether stdout is echoed while still being captured.quietsuppresses stderr mirroring (stderr is always returned in the artifacts).RUST_LOGdefaults toerrorfor these subcommands when the environment is unset; setRUST_LOG=info(or higher) to inspect codex internals.
use CodexClient;
# async
RUST_LOG defaults
If RUST_LOG is unset, the wrapper injects RUST_LOG=error for spawned commands to silence verbose upstream tracing. Any existing RUST_LOG value is respected.
MCP + app-server helpers
codex::mcpoffers typed clients forcodex mcp-server --stdioandcodex app-server --stdio, along with config managers for[mcp_servers]and[app_runtimes]plus launcher helpers when you want to spawn from saved config.- Use
CodexClient::spawn_mcp_login_process(capability-guarded) when you need an interactive bearer token for HTTP transports before persisting it viaMcpConfigManager::login. - Examples:
mcp_codex_flow(typedtools/callforcodex+codex-replywith optional cancellation),mcp_codex_tool/mcp_codex_reply(raw tool calls with--samplepayloads; use thesession_idfromsession_configuredas theconversationId, and note thatcodex-replyrequires the session to remain active inside the samemcp-serverprocess on 0.61.0), andapp_server_turns/app_server_thread_turn(thread start/resume + optional interrupt). Pair these withfeature_detectionif the binary may be missing server endpoints. - MCP
codex-replydoes not rehydrate conversations from disk on 0.61.0; follow-up calls only work while the originalmcp-serverprocess is still running. For cross-process resumes, usecodex exec resume(CLI) or the app-serverthread/resumepath instead.
Runtime definitions and env prep
[mcp_servers]and[app_runtimes]live inconfig.toml;McpConfigManagerreads/writes them.StdioServerConfigshould be built with the Workstream A env prep (binary path,CODEX_HOME, base env, timeouts). Runtime entries layer env/timeout overrides on top of those defaults, andCODEX_HOMEis injected whencode_homeis set.- Resolution through the runtime/app APIs is read-only: stored config and metadata are not mutated.
MCP runtime API (read-only)
McpRuntimeApi::from_config(&manager, &defaults)loads launch-ready stdio configs or HTTP connectors from stored runtimes.availablereturnsMcpRuntimeSummaryentries (description/tags/tool hints + transport kind).launcher,stdio_launcher, andhttp_connectorhand back launchers/connectors without side effects; HTTP connectors resolve bearer tokens from env without overwriting existingAuthorizationheaders.preparespawns stdio runtimes or hands back HTTP connectors with tool hints preserved; useManagedStdioRuntime::stopto shut down processes (drop is best-effort kill).- Use
McpRuntimeManagerdirectly when you already have launchers and only need spawn/connector plumbing.
App runtime API (read-only)
AppRuntimeApi::from_config(&manager, &defaults)merges stored[app_runtimes]entries with defaults (binary/path/env/timeout) while keeping metadata/resume hints intact.availablelists stored runtimes and metadata;prepare/stdio_configreturn merged stdio configs without launching.startlaunches an app-server and returnsManagedAppRuntime(metadata + merged env +CodexAppServerhandle). Calls leave stored definitions untouched and preserve metadata for future starts.
Pooled app runtimes
AppRuntimePoolApi::from_config(&manager, &defaults)(orAppRuntimeApi::pool_api) wraps the pool that reuses running runtimes by name.availablelists stored entries;runninglists active runtimes;startreuses an existing process if one is already running;stop/stop_allclean up without altering stored definitions or metadata/resume hints.- Pool handles still expose stdio configs via
launcher/prepareso callers can inspect launch parameters without starting a process.
Examples and tests
examples/mcp_codex_flow.rs: startscodex mcp-server, streamscodex/event, supports$ /cancelRequestand follow-upcodex/codex-replyviatools/call; respectsCODEX_BINARY/CODEX_HOMEand does not touch stored[mcp_servers].examples/app_server_turns.rs: starts/resumescodex app-serverthreads, streams items/task_complete, and can issueturn/interruptafter the first item; metadata/thread IDs come from server responses and are not persisted by the wrapper.examples/responses_api_proxy.rs: launchescodex responses-api-proxywith an API key piped on stdin; falls back to a stub--samplepath when noOPENAI_API_KEY/CODEX_API_KEYis available and polls--server-infofor{port,pid}.examples/stdio_to_uds_live.rs: Unix-only live bridge that spins up a temp Unix socket listener, runscodex stdio-to-uds <socket>, sendsping, and prints the echoedpong.cargo test -p unified-agent-api-codexexercises env merging and non-destructive behavior (runtime_api_*,app_runtime_*,app_runtime_pool_*cover listing/prepare/start/stop without writing config or altering metadata).- See
crates/codex/EXAMPLES.mdfor one-to-one CLI parity examples, includingbundled_binary_hometo run Codex from an embedded binary with isolated state.
Integration notes
- For a practical integration pattern in an async shell/orchestrator (Substrate), see
docs/integrations/substrate.md.