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;
162pub mod protocol_generated;
163
164#[cfg(any(feature = "sync-client", feature = "async-client"))]
165pub mod cli;
166
167#[cfg(any(feature = "sync-client", feature = "async-client"))]
168pub mod version;
169
170#[cfg(any(feature = "sync-client", feature = "async-client"))]
171mod stderr_drain;
172
173#[cfg(feature = "sync-client")]
174pub mod client_sync;
175
176#[cfg(feature = "async-client")]
177pub mod client_async;
178
179// Exec-level event types (JSONL protocol)
180pub use io::events::{
181 ItemCompletedEvent, ItemStartedEvent, ItemUpdatedEvent, ThreadError, ThreadErrorEvent,
182 ThreadEvent, ThreadStartedEvent, TurnCompletedEvent, TurnFailedEvent, TurnStartedEvent, Usage,
183};
184
185// Thread item types (shared between exec and app-server)
186pub use io::items::{
187 AgentMessageItem, CommandExecutionItem, CommandExecutionStatus, ErrorItem, FileChangeItem,
188 FileUpdateChange, McpToolCallError, McpToolCallItem, McpToolCallResult, McpToolCallStatus,
189 PatchApplyStatus, PatchChangeKind, ReasoningItem, ThreadItem, TodoItem, TodoListItem,
190 WebSearchItem,
191};
192
193// Configuration types
194pub use io::options::{
195 ApprovalMode, ModelReasoningEffort, SandboxMode, ThreadOptions, WebSearchMode,
196};
197
198// Error types (always available)
199pub use error::{Error, ParseError, Result};
200
201// JSON-RPC types (always available)
202pub use jsonrpc::{
203 JsonRpcError, JsonRpcErrorData, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest,
204 JsonRpcResponse, RequestId,
205};
206
207// App-server protocol types (always available)
208pub use protocol::{
209 AccountLoginCompletedNotification, AccountRateLimitsUpdatedNotification,
210 AccountUpdatedNotification, AgentMessageDeltaNotification, AppListUpdatedNotification,
211 ClientInfo, CmdOutputDeltaNotification, CommandApprovalDecision,
212 CommandExecOutputDeltaNotification, CommandExecutionApprovalParams,
213 CommandExecutionApprovalResponse, ConfigWarningNotification, ContextCompactedNotification,
214 DeprecationNoticeNotification, ErrorNotification,
215 ExternalAgentConfigImportCompletedNotification, FileChangeApprovalDecision,
216 FileChangeApprovalParams, FileChangeApprovalResponse, FileChangeOutputDeltaNotification,
217 FileChangePatchUpdatedNotification, FsChangedNotification,
218 FuzzyFileSearchSessionCompletedNotification, FuzzyFileSearchSessionUpdatedNotification,
219 GuardianWarningNotification, HookCompletedNotification, HookStartedNotification,
220 InitializeCapabilities, InitializeParams, InitializeResponse, ItemCompletedNotification,
221 ItemGuardianApprovalReviewCompletedNotification, ItemGuardianApprovalReviewStartedNotification,
222 ItemStartedNotification, McpServerOauthLoginCompletedNotification,
223 McpServerStartupStatusUpdatedNotification, McpToolCallProgressNotification,
224 ModelReroutedNotification, ModelVerificationNotification, PlanDeltaNotification,
225 ProcessExitedNotification, ProcessOutputDeltaNotification, RateLimitWindow, RateLimits,
226 ReasoningDeltaNotification, ReasoningSummaryPartAddedNotification,
227 ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
228 ServerRequestResolvedNotification, SkillsChangedNotification, TerminalInteractionNotification,
229 ThreadArchiveParams, ThreadArchiveResponse, ThreadArchivedNotification,
230 ThreadClosedNotification, ThreadGoalClearedNotification, ThreadGoalUpdatedNotification,
231 ThreadInfo, ThreadNameUpdatedNotification, ThreadRealtimeClosedNotification,
232 ThreadRealtimeErrorNotification, ThreadRealtimeItemAddedNotification,
233 ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
234 ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
235 ThreadRealtimeTranscriptDoneNotification, ThreadStartParams, ThreadStartResponse,
236 ThreadStartedNotification, ThreadStatus, ThreadStatusChangedNotification,
237 ThreadTokenUsageUpdatedNotification, ThreadUnarchivedNotification, TokenCounts, TokenUsage,
238 Turn, TurnCompletedNotification, TurnDiffUpdatedNotification, TurnError, TurnInterruptParams,
239 TurnInterruptResponse, TurnPlanStep, TurnPlanStepStatus, TurnPlanUpdatedNotification,
240 TurnStartParams, TurnStartResponse, TurnStartedNotification, TurnStatus, UserInput,
241 WarningNotification, WindowsSandboxSetupCompletedNotification,
242 WindowsWorldWritableWarningNotification,
243};
244
245// Also re-export the `UserMessageItem` and its content block so callers can
246// pattern-match on the new ThreadItem variant added in 0.102.
247pub use io::items::{UserMessageContent, UserMessageItem};
248
249// Typed message dispatch (notifications + server-to-client requests)
250pub use messages::{Notification, ServerMessage, ServerRequest};
251
252// CLI builder (feature-gated)
253#[cfg(any(feature = "sync-client", feature = "async-client"))]
254pub use cli::AppServerBuilder;
255
256// Sync client
257#[cfg(feature = "sync-client")]
258pub use client_sync::{EventIterator, SyncClient};
259
260// Async client
261#[cfg(feature = "async-client")]
262pub use client_async::{AsyncClient, EventStream};