apiari-codex-sdk
Rust SDK for the OpenAI Codex CLI. Wraps the codex binary, reading JSONL events from stdout when invoked with codex exec --json.
This is not a direct API client. It spawns the codex CLI as a subprocess. The CLI handles authentication, tool execution, file access, and sandboxing.
Quick Start
use ;
async
Features
- Spawn and stream
codex exec --jsonsessions - Typed event stream with real-time incremental updates
- Resume previous sessions for multi-turn chat
- Full
ExecOptionscovering model, sandbox, approval policy, and more - Interrupt (SIGINT) and kill support
- Forward-compatible parsing — unknown event/item types deserialize as
Unknown
Requirements
- The
codexCLI must be installed and on$PATH(or provide a custom path viaCodexClient::with_cli_path)
Usage
Add to your Cargo.toml:
[]
= true
Execution Model — Unidirectional Single-Shot
The Codex SDK uses a unidirectional protocol. Each execution is a single prompt in, event stream out. There is no stdin — it is set to /dev/null.
┌─────────┐ CLI args (prompt) ┌───────────┐
│ SDK │ ────── exec() ────────────► │ codex │
│ │ │ CLI │
│ │ ◄──── next_event() ──────── │ (stdout) │
└─────────┘ JSONL stream └───────────┘
stdin = /dev/null
How a Single Execution Works
// 1. Start an execution — prompt goes as a CLI argument
let mut execution = client.exec.await?;
// 2. Read events as they stream in real-time
while let Some = execution.next_event.await?
// 3. Execution is done — process has exited
Multi-Turn Chat via Resume
To build a conversational UI, each user message is a separate execution that resumes the previous session using the thread ID:
// First message — new execution
let mut exec1 = client.exec.await?;
let mut thread_id = None;
while let Some = exec1.next_event.await?
// Second message — resumes the same session
let mut exec2 = client.exec_resume.await?;
while let Some = exec2.next_event.await?
Each exec_resume() creates a new subprocess that loads the previous session state. The conversation continues where it left off.
Key Properties
- One prompt per execution — the prompt is a CLI argument, not streamed via stdin.
- Events stream in real-time —
item.updatedgives incremental text for reasoning and messages. Render them live. - No mid-execution input — you cannot send messages or tool results during an execution. Input must be disabled in the UI while codex is working.
- Tool execution is internal — codex runs commands, edits files, and searches the web on its own. The SDK observes these as
CommandExecution,FileChange,WebSearchitems. interrupt()cancels the current execution (SIGINT), but you cannot redirect it — only stop it.- Resume for follow-ups — save the
thread_idfromThreadStartedand pass it toexec_resume()for the next message.
UI Implications
- Disable input while an execution is running. There is no way to send messages mid-execution.
- Re-enable input when
next_event()returnsNone(execution finished). - Render events incrementally —
item.updatedevents carry partial text that grows over time. - Show tool activity —
CommandExecution,FileChange,McpToolCallitems let you show what codex is doing.
Comparison with Claude SDK
| Codex SDK | Claude SDK | |
|---|---|---|
| Protocol | Unidirectional (stdout only) | Bidirectional (stdin + stdout) |
| Session lifetime | One subprocess per message | One subprocess for entire conversation |
| Mid-turn input | No (stdin is /dev/null) |
Yes (send_message, send_tool_result) |
| Tool execution | CLI handles tools internally | SDK receives tool requests, sends results |
| Multi-turn chat | Resume with session ID for each message | Send multiple messages on same session |
| Input availability | Disabled during execution | Always enabled |
Event Types
| Event | Description |
|---|---|
ThreadStarted |
Thread ID assigned — save for resume |
TurnStarted |
A new turn has begun |
TurnCompleted |
Turn finished (includes usage stats) |
TurnFailed |
Turn failed (includes error details) |
ItemStarted |
An item began (message, command, etc.) |
ItemUpdated |
Incremental update to an in-flight item |
ItemCompleted |
An item finished |
TokenCount |
Token usage statistics |
Error |
Execution-level error |
Item Types
| Item | Description |
|---|---|
AgentMessage |
Text response from the model |
Reasoning |
Chain-of-thought / thinking text |
CommandExecution |
Shell command with output and exit code |
FileChange |
File modifications with before/after content |
McpToolCall |
MCP tool invocation |
WebSearch |
Web search query |
TodoList |
Task list |
Error |
Item-level error |
Unknown |
Forward-compatibility catch-all |
Architecture
src/
lib.rs # Module declarations + re-exports
client.rs # CodexClient (factory) + Execution (read-only handle)
options.rs # ExecOptions, ResumeOptions, SandboxMode, ApprovalPolicy
transport.rs # ReadOnlyTransport (spawn, recv-only, interrupt, kill)
types.rs # Event, Item variants, Usage, supporting types
error.rs # SdkError enum + Result alias
tests/
integration.rs # Live CLI tests (#[ignore] by default)