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