Skip to main content

codex_codes/
lib.rs

1//! A typed Rust interface for the [OpenAI Codex CLI](https://github.com/openai/codex) protocol.
2//!
3//! This crate provides type-safe bindings for communicating with the Codex CLI's
4//! app-server via its JSON-RPC protocol. It handles message framing, request/response
5//! correlation, approval flows, and streaming notifications for multi-turn agent
6//! conversations.
7//!
8//! # Quick Start
9//!
10//! ```bash
11//! cargo add codex-codes
12//! ```
13//!
14//! ## Using the Async Client (Recommended)
15//!
16//! ```ignore
17//! use codex_codes::{AsyncClient, ThreadStartParams, TurnStartParams, UserInput, ServerMessage};
18//!
19//! #[tokio::main]
20//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
21//!     // Start the app-server (spawns `codex app-server --listen stdio://`)
22//!     let mut client = AsyncClient::start().await?;
23//!
24//!     // Create a thread (a conversation session)
25//!     let thread = client.thread_start(&ThreadStartParams::default()).await?;
26//!
27//!     // Send a turn (a user message that triggers an agent response)
28//!     client.turn_start(&TurnStartParams {
29//!         thread_id: thread.thread_id().to_string(),
30//!         input: vec![UserInput::Text { text: "What is 2 + 2?".into() }],
31//!         model: None,
32//!         reasoning_effort: None,
33//!         sandbox_policy: None,
34//!     }).await?;
35//!
36//!     // Stream notifications until the turn completes
37//!     while let Some(msg) = client.next_message().await? {
38//!         match msg {
39//!             ServerMessage::Notification { method, params } => {
40//!                 if method == "turn/completed" { break; }
41//!             }
42//!             ServerMessage::Request { id, .. } => {
43//!                 // Approval request — auto-accept for this example
44//!                 client.respond(id, &serde_json::json!({"decision": "accept"})).await?;
45//!             }
46//!         }
47//!     }
48//!
49//!     client.shutdown().await?;
50//!     Ok(())
51//! }
52//! ```
53//!
54//! ## Using the Sync Client
55//!
56//! ```ignore
57//! use codex_codes::{SyncClient, ThreadStartParams, TurnStartParams, UserInput, ServerMessage};
58//!
59//! fn main() -> Result<(), Box<dyn std::error::Error>> {
60//!     let mut client = SyncClient::start()?;
61//!     let thread = client.thread_start(&ThreadStartParams::default())?;
62//!
63//!     client.turn_start(&TurnStartParams {
64//!         thread_id: thread.thread_id().to_string(),
65//!         input: vec![UserInput::Text { text: "What is 2 + 2?".into() }],
66//!         model: None,
67//!         reasoning_effort: None,
68//!         sandbox_policy: None,
69//!     })?;
70//!
71//!     for result in client.events() {
72//!         match result? {
73//!             ServerMessage::Notification { method, .. } => {
74//!                 if method == "turn/completed" { break; }
75//!             }
76//!             _ => {}
77//!         }
78//!     }
79//!     Ok(())
80//! }
81//! ```
82//!
83//! # Architecture
84//!
85//! The crate is organized into several key modules:
86//!
87//! - [`client_async`] / [`client_sync`] — High-level clients that manage the
88//!   app-server process, request/response correlation, and message buffering
89//! - [`protocol`] — App-server v2 request params, response types, and notification
90//!   bodies (thread/turn lifecycle, approvals, deltas)
91//! - [`jsonrpc`] — Low-level JSON-RPC message types (request, response, error,
92//!   notification) matching the app-server's wire format
93//! - [`cli`] — Builder for spawning `codex app-server --listen stdio://`
94//! - [`error`] — Error types and result aliases
95//! - [`version`] — Version compatibility checking against the installed CLI
96//!
97//! # Protocol Overview
98//!
99//! The Codex app-server communicates via newline-delimited JSON-RPC 2.0 over stdio
100//! (without the standard `"jsonrpc":"2.0"` field). The conversation lifecycle is:
101//!
102//! 1. **Initialize** — `initialize` + `initialized` handshake (handled automatically by `start()`)
103//! 2. **Start a thread** — `thread/start` creates a conversation session
104//! 3. **Start a turn** — `turn/start` sends user input, triggering agent work
105//! 4. **Stream notifications** — The server emits `item/agentMessage/delta`,
106//!    `item/commandExecution/outputDelta`, etc. as the agent works
107//! 5. **Handle approvals** — The server may send requests like
108//!    `item/commandExecution/requestApproval` that require a response
109//! 6. **Turn completes** — `turn/completed` signals the agent is done
110//! 7. **Repeat** — Send another `turn/start` for follow-up questions
111//!
112//! # Feature Flags
113//!
114//! | Feature | Description | WASM-compatible |
115//! |---------|-------------|-----------------|
116//! | `types` | Core message types and protocol structs only | Yes |
117//! | `sync-client` | Synchronous client with blocking I/O | No |
118//! | `async-client` | Asynchronous client using tokio | No |
119//!
120//! All features are enabled by default. For WASM or type-sharing use cases:
121//!
122//! ```toml
123//! [dependencies]
124//! codex-codes = { version = "0.128", default-features = false, features = ["types"] }
125//! ```
126//!
127//! # Version Compatibility
128//!
129//! The Codex CLI protocol is evolving. This crate automatically checks your
130//! installed CLI version and warns if it's newer than tested. Current tested
131//! version: **0.130.0**
132//!
133//! Report compatibility issues at: <https://github.com/meawoppl/rust-code-agent-sdks/issues>
134//!
135//! # Examples
136//!
137//! See the `examples/` directory for complete working examples:
138//! - `async_client.rs` — Single-turn async query with streaming deltas
139//! - `sync_client.rs` — Single-turn synchronous query
140//! - `basic_repl.rs` — Interactive REPL with multi-turn conversation and approval handling
141//!
142//! # Parsing Raw Protocol Messages
143//!
144//! ```
145//! use codex_codes::{ThreadEvent, ThreadItem, JsonRpcMessage};
146//!
147//! // Parse exec-format JSONL events
148//! let json = r#"{"type":"thread.started","thread_id":"th_abc"}"#;
149//! let event: ThreadEvent = serde_json::from_str(json).unwrap();
150//!
151//! // Parse app-server JSON-RPC messages
152//! let rpc = r#"{"id":1,"result":{"threadId":"th_abc"}}"#;
153//! let msg: JsonRpcMessage = serde_json::from_str(rpc).unwrap();
154//! ```
155
156mod io;
157
158pub mod error;
159pub mod jsonrpc;
160pub mod messages;
161pub mod protocol;
162
163#[cfg(any(feature = "sync-client", feature = "async-client"))]
164pub mod cli;
165
166#[cfg(any(feature = "sync-client", feature = "async-client"))]
167pub mod version;
168
169#[cfg(any(feature = "sync-client", feature = "async-client"))]
170mod stderr_drain;
171
172#[cfg(feature = "sync-client")]
173pub mod client_sync;
174
175#[cfg(feature = "async-client")]
176pub mod client_async;
177
178// Exec-level event types (JSONL protocol)
179pub use io::events::{
180    ItemCompletedEvent, ItemStartedEvent, ItemUpdatedEvent, ThreadError, ThreadErrorEvent,
181    ThreadEvent, ThreadStartedEvent, TurnCompletedEvent, TurnFailedEvent, TurnStartedEvent, Usage,
182};
183
184// Thread item types (shared between exec and app-server)
185pub use io::items::{
186    AgentMessageItem, CommandExecutionItem, CommandExecutionStatus, ErrorItem, FileChangeItem,
187    FileUpdateChange, McpToolCallError, McpToolCallItem, McpToolCallResult, McpToolCallStatus,
188    PatchApplyStatus, PatchChangeKind, ReasoningItem, ThreadItem, TodoItem, TodoListItem,
189    WebSearchItem,
190};
191
192// Configuration types
193pub use io::options::{
194    ApprovalMode, ModelReasoningEffort, SandboxMode, ThreadOptions, WebSearchMode,
195};
196
197// Error types (always available)
198pub use error::{Error, ParseError, Result};
199
200// JSON-RPC types (always available)
201pub use jsonrpc::{
202    JsonRpcError, JsonRpcErrorData, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest,
203    JsonRpcResponse, RequestId,
204};
205
206// App-server protocol types (always available)
207pub use protocol::{
208    AccountLoginCompletedNotification, AccountRateLimitsUpdatedNotification,
209    AccountUpdatedNotification, AgentMessageDeltaNotification, AppListUpdatedNotification,
210    ClientInfo, CmdOutputDeltaNotification, CommandApprovalDecision,
211    CommandExecOutputDeltaNotification, CommandExecutionApprovalParams,
212    CommandExecutionApprovalResponse, ConfigWarningNotification, ContextCompactedNotification,
213    DeprecationNoticeNotification, ErrorNotification,
214    ExternalAgentConfigImportCompletedNotification, FileChangeApprovalDecision,
215    FileChangeApprovalParams, FileChangeApprovalResponse, FileChangeOutputDeltaNotification,
216    FileChangePatchUpdatedNotification, FsChangedNotification,
217    FuzzyFileSearchSessionCompletedNotification, FuzzyFileSearchSessionUpdatedNotification,
218    GuardianWarningNotification, HookCompletedNotification, HookStartedNotification,
219    InitializeCapabilities, InitializeParams, InitializeResponse, ItemCompletedNotification,
220    ItemGuardianApprovalReviewCompletedNotification, ItemGuardianApprovalReviewStartedNotification,
221    ItemStartedNotification, McpServerOauthLoginCompletedNotification,
222    McpServerStartupStatusUpdatedNotification, McpToolCallProgressNotification,
223    ModelReroutedNotification, ModelVerificationNotification, PlanDeltaNotification,
224    ProcessExitedNotification, ProcessOutputDeltaNotification, RateLimitWindow, RateLimits,
225    ReasoningDeltaNotification, ReasoningSummaryPartAddedNotification,
226    ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
227    ServerRequestResolvedNotification, SkillsChangedNotification, TerminalInteractionNotification,
228    ThreadArchiveParams, ThreadArchiveResponse, ThreadArchivedNotification,
229    ThreadClosedNotification, ThreadGoalClearedNotification, ThreadGoalUpdatedNotification,
230    ThreadInfo, ThreadNameUpdatedNotification, ThreadRealtimeClosedNotification,
231    ThreadRealtimeErrorNotification, ThreadRealtimeItemAddedNotification,
232    ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
233    ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
234    ThreadRealtimeTranscriptDoneNotification, ThreadStartParams, ThreadStartResponse,
235    ThreadStartedNotification, ThreadStatus, ThreadStatusChangedNotification,
236    ThreadTokenUsageUpdatedNotification, ThreadUnarchivedNotification, TokenCounts, TokenUsage,
237    Turn, TurnCompletedNotification, TurnDiffUpdatedNotification, TurnError, TurnInterruptParams,
238    TurnInterruptResponse, TurnPlanStep, TurnPlanStepStatus, TurnPlanUpdatedNotification,
239    TurnStartParams, TurnStartResponse, TurnStartedNotification, TurnStatus, UserInput,
240    WarningNotification, WindowsSandboxSetupCompletedNotification,
241    WindowsWorldWritableWarningNotification,
242};
243
244// Also re-export the `UserMessageItem` and its content block so callers can
245// pattern-match on the new ThreadItem variant added in 0.102.
246pub use io::items::{UserMessageContent, UserMessageItem};
247
248// Typed message dispatch (notifications + server-to-client requests)
249pub use messages::{Notification, ServerMessage, ServerRequest};
250
251// CLI builder (feature-gated)
252#[cfg(any(feature = "sync-client", feature = "async-client"))]
253pub use cli::AppServerBuilder;
254
255// Sync client
256#[cfg(feature = "sync-client")]
257pub use client_sync::{EventIterator, SyncClient};
258
259// Async client
260#[cfg(feature = "async-client")]
261pub use client_async::{AsyncClient, EventStream};