Skip to main content

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};