agent_sdk/lib.rs
1//! # Agent SDK
2//!
3//! A Rust SDK for building AI agents powered by large language models (LLMs).
4//!
5//! This crate provides the infrastructure to build agents that can:
6//! - Converse with users via multiple LLM providers
7//! - Execute tools to interact with external systems
8//! - Persist turn events for downstream consumers and UIs
9//! - Persist conversation history and state
10//!
11//! For task-oriented recipes — tools, typed tools, structured output,
12//! streaming, MCP (local + remote HTTP), durable serving, and
13//! human-in-the-loop — see the
14//! [cookbook](https://github.com/bipa-app/agent-sdk/blob/main/crates/agent-sdk/COOKBOOK.md)
15//! and the runnable
16//! [`examples/`](https://github.com/bipa-app/agent-sdk/tree/main/crates/agent-sdk/examples).
17//!
18//! ## Quick Start
19//!
20//! Ask a question and print the answer — the whole 30-second path:
21//!
22//! ```no_run
23//! use agent_sdk::{builder, ThreadId, providers::AnthropicProvider};
24//!
25//! # async fn example() -> anyhow::Result<()> {
26//! let agent = builder::<()>()
27//! .provider(AnthropicProvider::from_env()) // reads ANTHROPIC_API_KEY
28//! .build();
29//!
30//! let answer = agent.ask(ThreadId::new(), "What is the capital of France?").await?;
31//! println!("{answer}");
32//! # Ok(())
33//! # }
34//! ```
35//!
36//! [`ask`](AgentLoop::ask) builds the [`ToolContext`] and [`CancellationToken`]
37//! internally and returns the assembled assistant text. When you need
38//! application context, a confirmation flow, explicit cancellation, or the raw
39//! [`AgentRunState`], drop down to [`run`](AgentLoop::run). The runnable example
40//! below uses a tiny stub provider so it compiles **and runs** under
41//! `cargo test --doc` with no network and no API key:
42//!
43//! ```
44//! use std::sync::Arc;
45//! use agent_sdk::{
46//! builder, AgentEvent, AgentInput, CancellationToken, EventStore, InMemoryEventStore,
47//! ThreadId, ToolContext,
48//! };
49//! use agent_sdk::llm::{
50//! ChatOutcome, ChatRequest, ChatResponse, ContentBlock, LlmProvider, StopReason, Usage,
51//! };
52//! use async_trait::async_trait;
53//!
54//! // A stub provider that always replies with a fixed line. Keeps the
55//! // quickstart key-free and offline. Swap for `AnthropicProvider` (below)
56//! // for real conversations.
57//! struct StubProvider;
58//!
59//! #[async_trait]
60//! impl LlmProvider for StubProvider {
61//! async fn chat(&self, _request: ChatRequest) -> anyhow::Result<ChatOutcome> {
62//! Ok(ChatOutcome::Success(ChatResponse {
63//! id: "stub".to_string(),
64//! content: vec![ContentBlock::Text { text: "Paris.".to_string() }],
65//! model: self.model().to_string(),
66//! stop_reason: Some(StopReason::EndTurn),
67//! usage: Usage::default(),
68//! }))
69//! }
70//! fn model(&self) -> &str { "stub-model" }
71//! fn provider(&self) -> &'static str { "stub" }
72//! }
73//!
74//! # async fn example() -> anyhow::Result<()> {
75//! // 1. Build the agent.
76//! let event_store = Arc::new(InMemoryEventStore::new());
77//! let agent = builder::<()>()
78//! .provider(StubProvider)
79//! .event_store(event_store.clone())
80//! .build();
81//!
82//! // 2. Run a conversation.
83//! let thread_id = ThreadId::new();
84//! let final_state = agent.run(
85//! thread_id.clone(),
86//! AgentInput::Text("What is the capital of France?".to_string()),
87//! ToolContext::new(()),
88//! CancellationToken::new(),
89//! );
90//! let _ = final_state.await?;
91//!
92//! // 3. Read persisted events.
93//! let mut reply = String::new();
94//! for envelope in event_store.get_events(&thread_id).await? {
95//! match envelope.event {
96//! AgentEvent::Text { text, .. } => reply.push_str(&text),
97//! AgentEvent::Done { .. } => break,
98//! _ => {}
99//! }
100//! }
101//! assert_eq!(reply, "Paris.");
102//! # Ok(())
103//! # }
104//! # tokio::runtime::Builder::new_current_thread()
105//! # .enable_all()
106//! # .build()
107//! # .unwrap()
108//! # .block_on(example())
109//! # .unwrap();
110//! ```
111//!
112//! ### Run with a real key
113//!
114//! For a live conversation, depend on the `anthropic` provider and read your
115//! key from the environment — the only change is the provider line:
116//!
117//! ```no_run
118//! use agent_sdk::{builder, InMemoryEventStore, providers::AnthropicProvider};
119//! use std::sync::Arc;
120//!
121//! # fn main() -> anyhow::Result<()> {
122//! let api_key = std::env::var("ANTHROPIC_API_KEY")?;
123//! let event_store = Arc::new(InMemoryEventStore::new());
124//! let agent = builder::<()>()
125//! .provider(AnthropicProvider::sonnet(api_key))
126//! .event_store(event_store)
127//! .build();
128//! # let _ = agent;
129//! # Ok(())
130//! # }
131//! ```
132//!
133//! ## Core Concepts
134//!
135//! ### Agent Loop
136//!
137//! The [`AgentLoop`] orchestrates the conversation cycle:
138//!
139//! 1. User sends a message
140//! 2. Agent sends message to LLM
141//! 3. LLM responds with text and/or tool calls
142//! 4. Agent executes tools and feeds results back to LLM
143//! 5. Repeat until LLM responds with only text
144//!
145//! Use [`builder()`] to construct an agent:
146//!
147//! ```no_run
148//! use agent_sdk::{builder, AgentConfig, providers::AnthropicProvider};
149//!
150//! # fn example() {
151//! let agent = builder::<()>()
152//! .provider(AnthropicProvider::from_env())
153//! .config(AgentConfig {
154//! max_turns: Some(20),
155//! system_prompt: "You are a helpful assistant.".into(),
156//! ..Default::default()
157//! })
158//! .build();
159//! # }
160//! ```
161//!
162//! ### Tools
163//!
164//! Tools let the LLM interact with external systems. The lowest-ceremony way
165//! is the [`SimpleTool`] trait — a `&'static str` name, no [`ToolName`] type:
166//!
167//! ```
168//! use agent_sdk::{SimpleTool, ToolContext, ToolResult};
169//! use serde_json::{json, Value};
170//! use std::future::Future;
171//!
172//! struct WeatherTool;
173//!
174//! impl SimpleTool<()> for WeatherTool {
175//! fn name(&self) -> &'static str { "get_weather" }
176//! fn description(&self) -> &'static str { "Get current weather for a city" }
177//! fn input_schema(&self) -> Value {
178//! json!({ "type": "object", "properties": { "city": { "type": "string" } } })
179//! }
180//!
181//! fn execute(
182//! &self,
183//! _ctx: &ToolContext<()>,
184//! input: Value,
185//! ) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
186//! async move {
187//! let city = input["city"].as_str().unwrap_or("Unknown");
188//! Ok(ToolResult::success(format!("Weather in {city}: Sunny, 72°F")))
189//! }
190//! }
191//! }
192//! ```
193//!
194//! Register it with [`ToolRegistry::register_simple`]:
195//!
196//! ```no_run
197//! use agent_sdk::{builder, ToolRegistry, providers::AnthropicProvider};
198//! # use agent_sdk::{SimpleTool, ToolContext, ToolResult};
199//! # use serde_json::Value;
200//! # use std::future::Future;
201//! # struct WeatherTool;
202//! # impl SimpleTool<()> for WeatherTool {
203//! # fn name(&self) -> &'static str { "get_weather" }
204//! # fn description(&self) -> &'static str { "" }
205//! # fn input_schema(&self) -> Value { Value::Null }
206//! # fn execute(&self, _: &ToolContext<()>, _: Value) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
207//! # async { Ok(ToolResult::success("")) }
208//! # }
209//! # }
210//!
211//! # fn example() {
212//! let mut tools = ToolRegistry::new();
213//! tools.register_simple(WeatherTool);
214//!
215//! let agent = builder::<()>()
216//! .provider(AnthropicProvider::from_env())
217//! .tools(tools)
218//! .build();
219//! # }
220//! ```
221//!
222//! For full control over the serialized tool name, implement the [`Tool`]
223//! trait directly with a strongly-typed [`ToolName`] (e.g. a `#[derive(Serialize,
224//! Deserialize)]` enum or the built-in [`DynamicToolName`]).
225//!
226//! ### Tool Tiers
227//!
228//! Tools are classified by permission level via [`ToolTier`]:
229//!
230//! | Tier | Description | Example |
231//! |------|-------------|---------|
232//! | [`ToolTier::Observe`] | Read-only, always allowed | Get balance, read file |
233//! | [`ToolTier::Confirm`] | Requires user confirmation | Send email, transfer funds |
234//!
235//! ### Lifecycle Hooks
236//!
237//! Implement [`AgentHooks`] to intercept and control agent behavior:
238//!
239//! ```
240//! use agent_sdk::{AgentHooks, ToolDecision, ToolInvocation, ToolResult, ToolTier};
241//! use async_trait::async_trait;
242//!
243//! struct MyHooks;
244//!
245//! #[async_trait]
246//! impl AgentHooks for MyHooks {
247//! async fn pre_tool_use(&self, invocation: &ToolInvocation) -> ToolDecision {
248//! println!("Tool called: {}", invocation.tool_name);
249//! match invocation.tier {
250//! ToolTier::Observe => ToolDecision::Allow,
251//! ToolTier::Confirm => ToolDecision::RequiresConfirmation(
252//! "Please confirm this action".into()
253//! ),
254//! }
255//! }
256//!
257//! async fn post_tool_use(&self, tool_name: &str, result: &ToolResult) {
258//! println!("{tool_name} completed: {}", result.success);
259//! }
260//! }
261//! ```
262//!
263//! Built-in hook implementations:
264//! - [`DefaultHooks`] - Tier-based permissions (default)
265//! - [`AllowAllHooks`] - Allow all tools without confirmation (for testing)
266//! - [`LoggingHooks`] - Debug logging for all events
267//!
268//! ### Events
269//!
270//! The agent emits [`AgentEvent`]s during execution for real-time updates:
271//!
272//! | Event | Description |
273//! |-------|-------------|
274//! | [`AgentEvent::Start`] | Agent begins processing |
275//! | [`AgentEvent::Text`] | Text response from LLM |
276//! | [`AgentEvent::TextDelta`] | Streaming text chunk |
277//! | [`AgentEvent::ToolCallStart`] | Tool execution starting |
278//! | [`AgentEvent::ToolCallEnd`] | Tool execution completed |
279//! | [`AgentEvent::TurnComplete`] | One LLM round-trip finished |
280//! | [`AgentEvent::Done`] | Agent completed successfully |
281//! | [`AgentEvent::Error`] | An error occurred |
282//!
283//! ### Task Tracking
284//!
285//! Use [`TodoWriteTool`] and [`TodoReadTool`] to track task progress:
286//!
287//! ```no_run
288//! use agent_sdk::todo::{TodoState, TodoWriteTool, TodoReadTool};
289//! use std::sync::Arc;
290//! use tokio::sync::RwLock;
291//!
292//! let state = Arc::new(RwLock::new(TodoState::new()));
293//! let write_tool = TodoWriteTool::new(Arc::clone(&state));
294//! let read_tool = TodoReadTool::new(state);
295//! ```
296//!
297//! Task states: `Pending` (○), `InProgress` (⚡), `Completed` (✓)
298//!
299//! ### Custom Context
300//!
301//! Pass application-specific data to tools via the generic type parameter:
302//!
303//! ```
304//! use agent_sdk::{DynamicToolName, Tool, ToolContext, ToolResult, ToolTier};
305//! use serde_json::Value;
306//! use std::future::Future;
307//!
308//! // Your application context
309//! struct AppContext {
310//! user_id: String,
311//! // database: Database,
312//! }
313//!
314//! struct UserInfoTool;
315//!
316//! impl Tool<AppContext> for UserInfoTool {
317//! type Name = DynamicToolName;
318//!
319//! fn name(&self) -> DynamicToolName { DynamicToolName::new("get_user_info") }
320//! fn display_name(&self) -> &'static str { "User Info" }
321//! fn description(&self) -> &'static str { "Get info about current user" }
322//! fn input_schema(&self) -> Value { serde_json::json!({"type": "object"}) }
323//!
324//! fn execute(
325//! &self,
326//! ctx: &ToolContext<AppContext>,
327//! _input: Value,
328//! ) -> impl Future<Output = anyhow::Result<ToolResult>> + Send {
329//! let user_id = ctx.app.user_id.clone();
330//! async move {
331//! Ok(ToolResult::success(format!("User: {user_id}")))
332//! }
333//! }
334//! }
335//! ```
336//!
337//! ## Workspace Architecture
338//!
339//! The SDK is split into focused crates. This `agent-sdk` crate is the
340//! **public façade** — it re-exports everything you need so downstream
341//! users only depend on `agent-sdk`.
342//!
343//! | Crate | Purpose |
344//! |-------|---------|
345//! | [`agent_sdk_foundation`] | Data-only contract types (IDs, events, LLM messages) |
346//! | [`agent_sdk_tools`] | Tool traits, registry, hooks, stores, environment |
347//! | [`agent_sdk_providers`] | LLM provider trait and first-party implementations |
348//! | `agent-server` | Server-side orchestration (internal, not published) |
349//! | **`agent-sdk`** | **This crate** — façade with agent loop, examples, and convenience re-exports |
350//!
351//! ## Modules
352//!
353//! | Module | Description |
354//! |--------|-------------|
355//! | [`providers`] | LLM provider implementations |
356//! | [`primitive_tools`] | Built-in file operation tools (Read, Write, Edit, Glob, Grep, Bash) |
357//! | [`llm`] | LLM abstraction layer |
358//! | [`subagent`] | Nested agent execution with [`SubagentFactory`] |
359//! | [`mcp`] | Model Context Protocol client (stdio + streamable-HTTP/SSE, resources/prompts) |
360//! | [`todo`](mod@todo) | Task tracking tools ([`TodoWriteTool`], [`TodoReadTool`]) |
361//! | [`user_interaction`] | User question/confirmation tools ([`AskUserQuestionTool`]) |
362//! | [`web`] | Web search and fetch tools |
363//! | [`skills`] | Custom skill/command loading |
364//! | [`reminders`] | System reminder infrastructure for agent guidance |
365//!
366//! ## System Reminders
367//!
368//! The SDK includes a reminder system that provides contextual guidance to the AI agent
369//! using the `<system-reminder>` XML tag pattern. Claude is trained to recognize these
370//! tags and follow the instructions without mentioning them to users.
371//!
372//! ```
373//! use agent_sdk::reminders::{wrap_reminder, ReminderConfig, ReminderTracker};
374//!
375//! // Wrap guidance in system-reminder tags
376//! let reminder = wrap_reminder("Verify the output before proceeding.");
377//!
378//! // Configure reminder behavior
379//! let config = ReminderConfig::new()
380//! .with_todo_reminder_turns(5)
381//! .with_repeated_action_threshold(3);
382//! ```
383//!
384//! ## Feature Flags
385//!
386//! Providers and the heavier tool families are gated behind cargo features so
387//! a minimal consumer only compiles (and only pulls the transitive
388//! dependencies of) what it uses. The common case — an Anthropic agent — works
389//! out of the box because `anthropic` is the only default feature.
390//!
391//! | Feature | Default | Pulls | Description |
392//! |---------|---------|-------|-------------|
393//! | `anthropic` | **Yes** | — | Anthropic Messages API provider |
394//! | `openai` | No | — | `OpenAI` Chat Completions + Responses providers |
395//! | `openai-codex` | No | `tokio-tungstenite` | `OpenAI` Codex / `ChatGPT` WebSocket provider |
396//! | `gemini` | No | — | Google Gemini provider |
397//! | `vertex` | No | — | Google Vertex AI provider (implies `anthropic` + `gemini`) |
398//! | `cloudflare` | No | — | Cloudflare AI Gateway proxy (implies `anthropic` + `openai` + `gemini`) |
399//! | `web` | No | `html2text` | [`web`] search + fetch tools |
400//! | `mcp` | No | — | [`mcp`] Model Context Protocol client (stdio + streamable-HTTP/SSE) |
401//! | `skills` | No | `serde_yaml_ng` | [`skills`] markdown skill loader |
402//! | `sqlite` | No | `rusqlite` (bundled) | Durable single-file [`SqliteStore`] session store |
403//! | `otel` | No | `opentelemetry` | OpenTelemetry tracing instrumentation |
404//!
405//! A minimal Anthropic-only build pulls no WebSocket, HTML, or YAML crates:
406//!
407//! ```toml
408//! agent-sdk = { version = "0.9", default-features = false, features = ["anthropic"] }
409//! ```
410//!
411//! When `otel` is enabled, the SDK emits OpenTelemetry spans for agent
412//! invocations, turns, LLM requests, tool execution, subagent runs, MCP
413//! operations, and context compaction. See the `observability` module for details.
414
415#![forbid(unsafe_code)]
416// Enable the `doc(cfg(...))` feature-badge annotations on docs.rs (which
417// builds with `--cfg docsrs` on nightly — see `[package.metadata.docs.rs]`).
418// A regular stable build never sets `docsrs`, so this nightly-only feature
419// flag is inert outside docs.rs.
420#![cfg_attr(docsrs, feature(doc_cfg))]
421
422// ── Private modules (owned by this crate) ────────────────────────────
423mod agent_loop;
424pub mod builtin_tools;
425mod capabilities;
426pub mod context;
427mod filesystem;
428pub mod primitive_tools;
429pub mod reminders;
430pub mod subagent;
431pub mod todo;
432pub mod user_interaction;
433
434// ── Feature-gated tool modules (opt-in, pull extra deps) ──────────────
435#[cfg(feature = "mcp")]
436#[cfg_attr(docsrs, doc(cfg(feature = "mcp")))]
437pub mod mcp;
438#[cfg(feature = "skills")]
439#[cfg_attr(docsrs, doc(cfg(feature = "skills")))]
440pub mod skills;
441#[cfg(feature = "web")]
442#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
443pub mod web;
444
445#[cfg(feature = "otel")]
446#[cfg_attr(docsrs, doc(cfg(feature = "otel")))]
447pub mod observability;
448
449// ── Re-export modules from workspace crates ──────────────────────────
450// These thin modules delegate to the extracted crates so that
451// `use agent_sdk::llm::*` etc. keep working for downstream users.
452mod authority;
453mod environment;
454mod events;
455mod hooks;
456pub mod llm;
457pub mod model_capabilities;
458pub mod pricing;
459pub mod providers;
460mod seed;
461mod stores;
462mod tools;
463mod types;
464
465// ── Flat re-exports ──────────────────────────────────────────────────
466// Grouped by source crate so the provenance is clear. The names kept at
467// the crate root are the newcomer-facing surface; server/host contract
468// types live under [`advanced`] so they don't dominate autocomplete or the
469// docs.rs front page.
470
471// agent-sdk (owned — agent loop)
472pub use agent_loop::{
473 AgentHandle, AgentLoop, AgentLoopBuilder, AgentLoopCompactionConfig, RunEventStream, RunStream,
474 builder,
475};
476pub use capabilities::AgentCapabilities;
477pub use filesystem::{InMemoryFileSystem, LocalFileSystem};
478pub use tokio_util::sync::CancellationToken;
479
480// agent-sdk-foundation (via thin modules)
481pub use agent_sdk_foundation::privacy::{
482 REDACTED_MARKER, RedactionLevel, RedactionPolicy, redact_error, redact_for_observability,
483 redact_string, redact_value,
484};
485pub use events::{AgentEvent, AgentEventEnvelope, SequenceCounter};
486pub use types::{
487 AgentConfig, AgentError, AgentInput, AgentRunState, AgentState, BudgetLimitKind,
488 ExecutionStatus, ExternalToolResult, PendingToolCallInfo, RetryConfig, RunOptions, ThreadId,
489 TokenUsage, ToolExecution, ToolInvocation, ToolOutcome, ToolResult, ToolRuntime, ToolTier,
490 TurnOptions, UsageLimits,
491};
492
493// agent-sdk-tools (via thin modules)
494pub use environment::{Environment, ExecResult, FileEntry, GrepMatch, NullEnvironment};
495pub use hooks::{
496 AgentHooks, AllowAllHooks, DefaultHooks, LoggingHooks, NoopAuditSink, RequestDecision,
497 ResponseDecision, ToolAuditSink, ToolDecision,
498};
499pub use seed::{DefaultContextFactory, ToolContextSeed};
500pub use stores::{
501 EventStore, InMemoryEventStore, InMemoryExecutionStore, InMemoryStore, MessageStore,
502 ObservingEventStore, StateStore, StoredTurnEvents, ToolExecutionStore,
503};
504// Durable, single-file SQLite-backed store implementing all four store traits.
505// Behind the `sqlite` feature so the default build pulls no SQLite dependency.
506#[cfg(feature = "sqlite")]
507#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
508pub use stores::sqlite::SqliteStore;
509pub use tools::{
510 AsyncTool, DynamicToolName, PrimitiveToolName, ProgressStage, SimpleTool, SimpleToolAdapter,
511 Tool, ToolContext, ToolLogic, ToolName, ToolRegistry, ToolStatus, TypedTool, TypedToolAdapter,
512 invalid_tool_input_result, stage_to_string, tool_name_from_str, tool_name_to_string,
513 validate_tool_input,
514};
515
516// ── Ergonomics macros (Phase 13·E) ───────────────────────────────────
517// `#[derive(Tool)]`, `#[derive(TypedTool)]`, and `#[derive(ToolName)]` are
518// re-exported from the proc-macro crate so a user writes
519// `use agent_sdk::Tool;` for both the trait and its derive (the trait/derive
520// share a name, which is idiomatic — cf. `serde::Serialize`). The generated
521// code refers to `::agent_sdk::…` paths and the `__macro_support` module
522// below, so consumers never depend on `agent-sdk-macros` directly. The
523// declarative `tool!` macro is defined in this crate (see the macro
524// definition further down).
525#[doc(inline)]
526pub use agent_sdk_macros::{Tool, ToolName, TypedTool};
527
528/// Re-exports the macros need at their expansion site. **Not** part of the
529/// stable public API — do not depend on it directly. It exists only so the
530/// `#[derive(...)]` output can name `serde_json`/`serde`/`anyhow`/`schemars`
531/// items through a single `::agent_sdk::__macro_support::…` path, regardless of
532/// which of those crates the consumer has in scope.
533#[doc(hidden)]
534pub mod __macro_support {
535 pub use anyhow::Result;
536 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
537 pub use serde_json::{Value, json, to_value};
538 // Re-export `serde` itself so the generated `#[serde(crate = "...")]`
539 // attribute on the `ToolName` mirror enum resolves without the consumer
540 // having `serde` in their dependency tree under that exact name.
541 pub use serde;
542
543 /// `schemars::schema_for` shim for `#[tool(schema = "derive")]`.
544 ///
545 /// Only present under the `macros-schema` feature; the macro emits a
546 /// `compile_error!` (not a missing-path error) when the feature is off, so
547 /// this absence is never the diagnostic a user sees.
548 #[cfg(feature = "macros-schema")]
549 #[must_use]
550 pub fn schema_for<T: schemars::JsonSchema>() -> schemars::Schema {
551 schemars::schema_for!(T)
552 }
553}
554
555/// Define a tool inline, expanding to a fresh zero-sized struct plus a
556/// [`SimpleTool`] impl — the lowest-ceremony way to add a one-off tool in an
557/// example, test, or script.
558///
559/// This is the declarative counterpart to [`derive@Tool`]: use the derive when
560/// you want a named, reusable tool type; reach for `tool!` when you just need a
561/// closure-like tool right where you register it.
562///
563/// The application context type defaults to `()`; pass `context: MyCtx,` before
564/// the closure to use a different one. The closure receives
565/// `&ToolContext<Ctx>` and the raw `serde_json::Value` arguments and must
566/// return a future resolving to `anyhow::Result<ToolResult>`.
567///
568/// # Example
569///
570/// ```
571/// use agent_sdk::{tool, ToolResult, ToolRegistry};
572/// use serde_json::json;
573///
574/// let weather = tool! {
575/// name: "get_weather",
576/// description: "Get the current weather for a city",
577/// schema: json!({
578/// "type": "object",
579/// "properties": { "city": { "type": "string" } },
580/// "required": ["city"],
581/// }),
582/// |_ctx, input| async move {
583/// let city = input["city"].as_str().unwrap_or("Unknown");
584/// Ok(ToolResult::success(format!("Weather in {city}: Sunny")))
585/// }
586/// };
587///
588/// let mut registry: ToolRegistry<()> = ToolRegistry::new();
589/// registry.register_simple(weather);
590/// ```
591#[macro_export]
592macro_rules! tool {
593 // Default context = ().
594 (
595 name: $name:expr,
596 description: $description:expr,
597 schema: $schema:expr,
598 | $ctx:ident , $input:ident | $body:expr $(,)?
599 ) => {
600 $crate::tool! {
601 name: $name,
602 description: $description,
603 schema: $schema,
604 context: (),
605 |$ctx, $input| $body
606 }
607 };
608 // Explicit context type.
609 (
610 name: $name:expr,
611 description: $description:expr,
612 schema: $schema:expr,
613 context: $ctxty:ty,
614 | $ctx:ident , $input:ident | $body:expr $(,)?
615 ) => {{
616 struct __InlineTool;
617
618 impl $crate::SimpleTool<$ctxty> for __InlineTool {
619 fn name(&self) -> &'static str {
620 $name
621 }
622
623 fn description(&self) -> &'static str {
624 $description
625 }
626
627 fn input_schema(&self) -> $crate::__macro_support::Value {
628 $schema
629 }
630
631 fn execute(
632 &self,
633 $ctx: &$crate::ToolContext<$ctxty>,
634 $input: $crate::__macro_support::Value,
635 ) -> impl ::core::future::Future<
636 Output = $crate::__macro_support::Result<$crate::ToolResult>,
637 > + ::core::marker::Send {
638 $body
639 }
640 }
641
642 __InlineTool
643 }};
644}
645
646// agent-sdk-providers (via thin modules)
647pub use llm::{
648 ContentBlock, ContentSource, Effort, LlmProvider, ServedSpeed, SpeedTier, ThinkingConfig,
649 ThinkingMode,
650};
651pub use model_capabilities::{
652 ModelCapabilities, PricePoint, Pricing, SourceStatus, get_model_capabilities,
653 supported_model_capabilities,
654};
655
656// Run-level cost budgeting: the pricing seam the loop consults ahead of the
657// static capability table.
658pub use pricing::CostEstimator;
659
660// Dynamic model discovery: the third-party capability/pricing feed and the
661// layered registry that resolves override → feed → static table. The registry
662// implements [`CostEstimator`], so it can be handed to
663// [`AgentLoopBuilder::cost_estimator`] to price runs from feed data.
664#[cfg(feature = "model-discovery")]
665pub use agent_sdk_providers::{
666 CatalogEntry, ModelCatalogSource, ModelRegistry, ModelsDevSource, OpenRouterSource,
667 PricingTier, ResolvedModel, ResolvedSource,
668};
669
670// Schema-validated structured output (Phase 13): the [`ResponseFormat`] request
671// field, the bounded re-prompt runner, and its typed result/error.
672pub use agent_sdk_foundation::llm::ResponseFormat;
673pub use agent_sdk_providers::{
674 StructuredConfig, StructuredOutput, StructuredOutputError, StructuredOutputSupport,
675 run_structured,
676};
677
678// ── Advanced / server-internal contract types ───────────────────────
679/// Server- and host-facing contract types.
680///
681/// These types are not needed by a typical in-process agent. They form the
682/// authoritative boundary that `agent-server` / `agent-service-host` build
683/// on: per-turn outcome and summary contracts, the durable continuation
684/// envelope, the audit-record protocol, the listen/erased tool plumbing, and
685/// the worker-context reconstruction factory.
686///
687/// They are grouped here so that `agent_sdk::` autocomplete and the docs.rs
688/// front page stay dominated by the newcomer-facing surface (see
689/// [`prelude`]). Everything here remains a stable, public re-export — moving
690/// a name into `advanced` is a path change, not a removal.
691pub mod advanced {
692 // Per-turn outcome / summary contract and the durable continuation.
693 pub use crate::types::{
694 AgentContinuation, CONTINUATION_VERSION, ContinuationEnvelope, ListenExecutionContext,
695 TurnOutcome, TurnSummary,
696 };
697
698 // Audit-record protocol emitted at every tool-lifecycle transition.
699 pub use agent_sdk_foundation::audit::{
700 AuditProvenance, ToolAuditOutcome, ToolAuditRecord, ToolAuditRecordParams,
701 };
702
703 // Event-sequencing authority used by the server commit path.
704 pub use crate::authority::{EventAuthority, LocalEventAuthority};
705
706 // Worker-context reconstruction for externalized tool runtimes.
707 pub use crate::seed::{ExecutionContextFactory, HostDependencies};
708
709 // Listen/execute tool protocol and the type-erased registry wrappers.
710 pub use crate::tools::{
711 ErasedAsyncTool, ErasedListenTool, ErasedTool, ErasedToolStatus, ListenExecuteTool,
712 ListenStopReason, ListenToolUpdate,
713 };
714}
715
716// ── Prelude ──────────────────────────────────────────────────────────
717/// The common imports for building an in-process agent.
718///
719/// `use agent_sdk::prelude::*;` brings the ~dozen names a newcomer needs:
720/// the [`builder`], configuration and I/O types, the [`Tool`] surface, the
721/// in-memory event store, the cancellation token, and — when the `anthropic`
722/// feature is enabled (the default) — the
723/// [`AnthropicProvider`](crate::providers::AnthropicProvider). Server-only
724/// contract types are intentionally excluded — reach for [`crate::advanced`]
725/// when you need them.
726pub mod prelude {
727 pub use crate::builder;
728 #[cfg(feature = "anthropic")]
729 pub use crate::providers::AnthropicProvider;
730 // `tool!` (declarative) plus the `Tool` / `TypedTool` / `ToolName` derives
731 // ride along on the same-named trait re-exports below — importing the trait
732 // also imports the derive macro (cf. `serde::Serialize`).
733 pub use crate::tool;
734 pub use crate::{
735 AgentConfig, AgentEvent, AgentInput, CancellationToken, DynamicToolName,
736 InMemoryEventStore, SimpleTool, Tool, ToolContext, ToolName, ToolRegistry, ToolResult,
737 ToolTier, TypedTool,
738 };
739}
740
741// Convenience re-exports
742pub use reminders::{
743 ReminderConfig, ReminderTracker, ReminderTrigger, ToolReminder, append_reminder, wrap_reminder,
744};
745pub use subagent::{
746 METADATA_MAX_SUBAGENT_DEPTH, METADATA_SUBAGENT_DEPTH, SubagentConfig, SubagentFactory,
747 SubagentTool,
748};
749pub use todo::{TodoItem, TodoReadTool, TodoState, TodoStatus, TodoWriteTool};
750pub use user_interaction::{
751 AskUserQuestionTool, ConfirmationRequest, ConfirmationResponse, QuestionOption,
752 QuestionRequest, QuestionResponse,
753};
754
755#[cfg(feature = "otel")]
756#[cfg_attr(docsrs, doc(cfg(feature = "otel")))]
757pub use observability::{
758 CaptureDecision, CaptureKind, CaptureResult, ObservabilityStore, PayloadBundle,
759};