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//! See `examples/async_client.rs` and `examples/sync_client.rs` for runnable
15//! versions of the usage patterns below. The high-level shape:
16//!
17//! ```ignore
18//! let mut client = AsyncClient::start().await?;
19//! let thread = client
20//!     .thread_start(&serde_json::from_value(serde_json::json!({}))?)
21//!     .await?;
22//! client.turn_start(&TurnStartParams {
23//!     thread_id: thread.thread.id.clone(),
24//!     input: vec![UserInput::Text {
25//!         text: "What is 2 + 2?".into(),
26//!         text_elements: None,
27//!     }],
28//!     // All other fields default to None — populate when overriding
29//!     // model / approval / sandbox / etc. for this turn.
30//!     ..serde_json::from_value(serde_json::json!({"threadId": thread.thread.id, "input": []}))?
31//! }).await?;
32//! ```
33//!
34//! # Architecture
35//!
36//! The crate is organized into several key modules:
37//!
38//! - [`client_async`] / [`client_sync`] — High-level clients that manage the
39//!   app-server process, request/response correlation, and message buffering
40//! - [`protocol`] — App-server v2 request params, response types, and notification
41//!   bodies (thread/turn lifecycle, approvals, deltas)
42//! - [`jsonrpc`] — Low-level JSON-RPC message types (request, response, error,
43//!   notification) matching the app-server's wire format
44//! - [`cli`] — Builder for spawning `codex app-server --listen stdio://`
45//! - [`error`] — Error types and result aliases
46//! - [`version`] — Version compatibility checking against the installed CLI
47//!
48//! # Protocol Overview
49//!
50//! The Codex app-server communicates via newline-delimited JSON-RPC 2.0 over stdio
51//! (without the standard `"jsonrpc":"2.0"` field). The conversation lifecycle is:
52//!
53//! 1. **Initialize** — `initialize` + `initialized` handshake (handled automatically by `start()`)
54//! 2. **Start a thread** — `thread/start` creates a conversation session
55//! 3. **Start a turn** — `turn/start` sends user input, triggering agent work
56//! 4. **Stream notifications** — The server emits `item/agentMessage/delta`,
57//!    `item/commandExecution/outputDelta`, etc. as the agent works
58//! 5. **Handle approvals** — The server may send requests like
59//!    `item/commandExecution/requestApproval` that require a response
60//! 6. **Turn completes** — `turn/completed` signals the agent is done
61//! 7. **Repeat** — Send another `turn/start` for follow-up questions
62//!
63//! # Feature Flags
64//!
65//! | Feature | Description | WASM-compatible |
66//! |---------|-------------|-----------------|
67//! | `types` | Core message types and protocol structs only | Yes |
68//! | `sync-client` | Synchronous client with blocking I/O | No |
69//! | `async-client` | Asynchronous client using tokio | No |
70//!
71//! All features are enabled by default. For WASM or type-sharing use cases:
72//!
73//! ```toml
74//! [dependencies]
75//! codex-codes = { version = "0.128", default-features = false, features = ["types"] }
76//! ```
77//!
78//! # Version Compatibility
79//!
80//! The Codex CLI protocol is evolving. This crate automatically checks your
81//! installed CLI version and warns if it's newer than tested. Current tested
82//! version: **0.130.0**
83//!
84//! Report compatibility issues at: <https://github.com/meawoppl/rust-code-agent-sdks/issues>
85//!
86//! # Examples
87//!
88//! See the `examples/` directory for complete working examples:
89//! - `async_client.rs` — Single-turn async query with streaming deltas
90//! - `sync_client.rs` — Single-turn synchronous query
91//! - `basic_repl.rs` — Interactive REPL with multi-turn conversation and approval handling
92//!
93//! # Parsing Raw Protocol Messages
94//!
95//! ```
96//! use codex_codes::{ThreadEvent, ThreadItem, JsonRpcMessage};
97//!
98//! // Parse exec-format JSONL events
99//! let json = r#"{"type":"thread.started","thread_id":"th_abc"}"#;
100//! let event: ThreadEvent = serde_json::from_str(json).unwrap();
101//!
102//! // Parse app-server JSON-RPC messages
103//! let rpc = r#"{"id":1,"result":{"threadId":"th_abc"}}"#;
104//! let msg: JsonRpcMessage = serde_json::from_str(rpc).unwrap();
105//! ```
106
107pub mod io;
108
109pub mod error;
110pub mod jsonrpc;
111pub mod messages;
112pub mod protocol;
113pub mod protocol_generated;
114
115#[cfg(any(feature = "sync-client", feature = "async-client"))]
116pub mod cli;
117
118#[cfg(any(feature = "sync-client", feature = "async-client"))]
119pub mod version;
120
121#[cfg(any(feature = "sync-client", feature = "async-client"))]
122mod stderr_drain;
123
124#[cfg(feature = "sync-client")]
125pub mod client_sync;
126
127#[cfg(feature = "async-client")]
128pub mod client_async;
129
130// Exec-level event types (JSONL protocol)
131pub use io::events::{
132    ItemCompletedEvent, ItemStartedEvent, ItemUpdatedEvent, ThreadError, ThreadErrorEvent,
133    ThreadEvent, ThreadStartedEvent, TurnCompletedEvent, TurnFailedEvent, TurnStartedEvent, Usage,
134};
135
136// Error types (always available)
137pub use error::{Error, ParseError, Result};
138
139// JSON-RPC types (always available)
140pub use jsonrpc::{
141    JsonRpcError, JsonRpcErrorData, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest,
142    JsonRpcResponse, RequestId,
143};
144
145// App-server protocol types — generated from the upstream JSON Schema and
146// re-exported through `protocol::*`.
147pub use protocol::*;
148
149// Typed message dispatch (notifications + server-to-client requests)
150pub use messages::{Notification, ServerMessage, ServerRequest};
151
152// CLI builder (feature-gated)
153#[cfg(any(feature = "sync-client", feature = "async-client"))]
154pub use cli::AppServerBuilder;
155
156// Sync client
157#[cfg(feature = "sync-client")]
158pub use client_sync::{EventIterator, SyncClient};
159
160// Async client
161#[cfg(feature = "async-client")]
162pub use client_async::{AsyncClient, EventStream};