Skip to main content

agent_abstraction/
lib.rs

1//! Drive Claude Code, Codex and GitHub Copilot headlessly from Rust.
2//!
3//! One request type, one event vocabulary and one session model across three
4//! agent CLIs that agree on none of those things. This is a **library**: your
5//! program links it and spawns the agent itself, with no intermediate CLI
6//! marshalling a request through stdout and back.
7//!
8//! # Running a prompt
9//!
10//! ```no_run
11//! use agent_abstraction::{Agent, Permission, Request, run};
12//!
13//! # async fn example() -> agent_abstraction::Result<()> {
14//! let outcome = run(
15//!     &Request::new(Agent::Claude, "Reply with the single word: pong")
16//!         .model("haiku")
17//!         .permission(Permission::ReadOnly),
18//! )
19//! .await?;
20//!
21//! println!("{}", outcome.text);
22//! # Ok(())
23//! # }
24//! ```
25//!
26//! # Watching one as it works
27//!
28//! ```no_run
29//! use agent_abstraction::{Agent, Event, Request, stream};
30//!
31//! # async fn example() -> agent_abstraction::Result<()> {
32//! let mut running = stream(&Request::new(Agent::Claude, "audit this repo"))?;
33//! while let Some(event) = running.recv().await {
34//!     match event {
35//!         Event::Text(text) => print!("{text}"),
36//!         Event::ToolCall { name, .. } => println!("[{name}]"),
37//!         _ => {}
38//!     }
39//! }
40//! let outcome = running.finish().await?;
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! # Multi-turn conversations
46//!
47//! Thread one stable name across turns and let [`SessionStore`] map it to
48//! whatever handle the agent understands:
49//!
50//! ```no_run
51//! use agent_abstraction::{Agent, Request, SessionStore, run};
52//!
53//! # async fn example() -> agent_abstraction::Result<()> {
54//! let store = SessionStore::open("/var/lib/myapp/sessions");
55//!
56//! // First turn creates the session; later turns continue it.
57//! let first = Request::new(Agent::Claude, "remember the number 7")
58//!     .session(&store, ".", "thread-42", false)?;
59//! run(&first).await?;
60//!
61//! let second = Request::new(Agent::Claude, "what number did I say?")
62//!     .session(&store, ".", "thread-42", false)?;
63//! println!("{}", run(&second).await?.text);
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! # What each agent can do
69//!
70//! | | session id | fork | events | system prompt |
71//! |---|---|---|---|---|
72//! | Claude Code | caller-minted (`--session-id`) | yes | yes | native flag |
73//! | Codex | agent-printed (`thread_id`) | no | yes | prepended |
74//! | Copilot | caller-minted (`--session-id`) | no | yes | prepended |
75//!
76//! Asking for something an agent cannot do is always an [`Error::Unsupported`],
77//! never a silent downgrade. A caller that asked to fork and got a linear
78//! resume would corrupt the conversation it meant to branch.
79//!
80//! # Operating within the agents' terms
81//!
82//! This crate drives each vendor's own supported headless interface with the
83//! credentials that CLI already uses. It does not reimplement a provider API,
84//! multiplex accounts, or retry around a quota: a refusal surfaces as
85//! [`Error::RateLimited`], carrying the provider's own wording, and backing off
86//! is the caller's decision. See `docs/operating-limits.md`.
87
88mod account;
89mod agent;
90mod auth;
91mod error;
92mod event;
93mod model;
94mod outcome;
95mod probe;
96mod proc;
97mod request;
98mod run;
99mod session;
100
101pub use account::{AccountUsage, Credits, DailyUsage, Lifetime, UsageWindow};
102pub use agent::{Agent, Caps, EnvPolicy, Format, NETWORK_ENV, Permission, SessionSupport};
103pub use auth::{AuthState, AuthStatus};
104pub use error::{Error, Result};
105pub use event::{Event, MAX_CAPTURE, MAX_EVENT_BYTES, MAX_LINE, TRUNCATION_MARK};
106pub use model::{Kind, Model, Source, Verified};
107pub use outcome::{Outcome, RateLimit, Stop, Usage};
108pub use probe::{Probe, Version, VersionStatus};
109pub use request::Request;
110pub use run::{Run, run, stream};
111pub use session::{Phase, SessionRecord, SessionStore};