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//! | `sqlite` | No | `rusqlite` (bundled) | Durable single-file [`SqliteStore`] session store |
408//! | `otel` | No | `opentelemetry` | OpenTelemetry tracing instrumentation |
409//!
410//! A minimal Anthropic-only build pulls no WebSocket, HTML, or YAML crates:
411//!
412//! ```toml
413//! agent-sdk = { version = "0.9", default-features = false, features = ["anthropic"] }
414//! ```
415//!
416//! When `otel` is enabled, the SDK emits OpenTelemetry spans for agent
417//! invocations, turns, LLM requests, tool execution, subagent runs, MCP
418//! operations, and context compaction. See the `observability` module for details.
419
420#![forbid(unsafe_code)]
421// Enable the `doc(cfg(...))` feature-badge annotations on docs.rs (which
422// builds with `--cfg docsrs` on nightly — see `[package.metadata.docs.rs]`).
423// A regular stable build never sets `docsrs`, so this nightly-only feature
424// flag is inert outside docs.rs.
425#![cfg_attr(docsrs, feature(doc_cfg))]
426
427// ── Private modules (owned by this crate) ────────────────────────────
428mod agent_loop;
429pub mod builtin_tools;
430mod capabilities;
431pub mod context;
432mod filesystem;
433pub mod primitive_tools;
434pub mod reminders;
435pub mod subagent;
436pub mod todo;
437pub mod user_interaction;
438
439// ── Feature-gated tool modules (opt-in, pull extra deps) ──────────────
440#[cfg(feature = "mcp")]
441#[cfg_attr(docsrs, doc(cfg(feature = "mcp")))]
442pub mod mcp;
443#[cfg(feature = "skills")]
444#[cfg_attr(docsrs, doc(cfg(feature = "skills")))]
445pub mod skills;
446#[cfg(feature = "web")]
447#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
448pub mod web;
449
450#[cfg(feature = "otel")]
451#[cfg_attr(docsrs, doc(cfg(feature = "otel")))]
452pub mod observability;
453
454// ── Re-export modules from workspace crates ──────────────────────────
455// These thin modules delegate to the extracted crates so that
456// `use agent_sdk::llm::*` etc. keep working for downstream users.
457mod authority;
458mod environment;
459mod events;
460mod hooks;
461pub mod llm;
462pub mod model_capabilities;
463pub mod providers;
464mod seed;
465mod stores;
466mod tools;
467mod types;
468
469// ── Flat re-exports ──────────────────────────────────────────────────
470// Grouped by source crate so the provenance is clear. The names kept at
471// the crate root are the newcomer-facing surface; server/host contract
472// types live under [`advanced`] so they don't dominate autocomplete or the
473// docs.rs front page.
474
475// agent-sdk (owned — agent loop)
476pub use agent_loop::{
477 AgentHandle, AgentLoop, AgentLoopBuilder, AgentLoopCompactionConfig, RunEventStream, RunStream,
478 builder,
479};
480pub use capabilities::AgentCapabilities;
481pub use filesystem::{InMemoryFileSystem, LocalFileSystem};
482pub use tokio_util::sync::CancellationToken;
483
484// agent-sdk-foundation (via thin modules)
485pub use agent_sdk_foundation::privacy::{
486 REDACTED_MARKER, RedactionLevel, RedactionPolicy, redact_error, redact_for_observability,
487 redact_string, redact_value,
488};
489pub use events::{AgentEvent, AgentEventEnvelope, SequenceCounter};
490pub use types::{
491 AgentConfig, AgentError, AgentInput, AgentRunState, AgentState, BudgetLimitKind,
492 ExecutionStatus, ExternalToolResult, PendingToolCallInfo, RetryConfig, RunOptions, ThreadId,
493 TokenUsage, ToolExecution, ToolInvocation, ToolOutcome, ToolResult, ToolRuntime, ToolTier,
494 TurnOptions, UsageLimits,
495};
496
497// agent-sdk-tools (via thin modules)
498pub use environment::{Environment, ExecResult, FileEntry, GrepMatch, NullEnvironment};
499pub use hooks::{
500 AgentHooks, AllowAllHooks, DefaultHooks, LoggingHooks, NoopAuditSink, RequestDecision,
501 ResponseDecision, ToolAuditSink, ToolDecision,
502};
503pub use seed::{DefaultContextFactory, ToolContextSeed};
504pub use stores::{
505 EventStore, InMemoryEventStore, InMemoryExecutionStore, InMemoryStore, MessageStore,
506 ObservingEventStore, StateStore, StoredTurnEvents, ToolExecutionStore,
507};
508// Durable, single-file SQLite-backed store implementing all four store traits.
509// Behind the `sqlite` feature so the default build pulls no SQLite dependency.
510#[cfg(feature = "sqlite")]
511#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
512pub use stores::sqlite::SqliteStore;
513pub use tools::{
514 AsyncTool, DynamicToolName, PrimitiveToolName, ProgressStage, SimpleTool, SimpleToolAdapter,
515 Tool, ToolContext, ToolLogic, ToolName, ToolRegistry, ToolStatus, TypedTool, TypedToolAdapter,
516 invalid_tool_input_result, stage_to_string, tool_name_from_str, tool_name_to_string,
517 validate_tool_input,
518};
519
520// ── Ergonomics macros (Phase 13·E) ───────────────────────────────────
521// `#[derive(Tool)]`, `#[derive(TypedTool)]`, and `#[derive(ToolName)]` are
522// re-exported from the proc-macro crate so a user writes
523// `use agent_sdk::Tool;` for both the trait and its derive (the trait/derive
524// share a name, which is idiomatic — cf. `serde::Serialize`). The generated
525// code refers to `::agent_sdk::…` paths and the `__macro_support` module
526// below, so consumers never depend on `agent-sdk-macros` directly. The
527// declarative `tool!` macro is defined in this crate (see the macro
528// definition further down).
529#[doc(inline)]
530pub use agent_sdk_macros::{Tool, ToolName, TypedTool};
531
532/// Re-exports the macros need at their expansion site. **Not** part of the
533/// stable public API — do not depend on it directly. It exists only so the
534/// `#[derive(...)]` output can name `serde_json`/`serde`/`anyhow`/`schemars`
535/// items through a single `::agent_sdk::__macro_support::…` path, regardless of
536/// which of those crates the consumer has in scope.
537#[doc(hidden)]
538pub mod __macro_support {
539 pub use anyhow::Result;
540 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
541 pub use serde_json::{Value, json, to_value};
542 // Re-export `serde` itself so the generated `#[serde(crate = "...")]`
543 // attribute on the `ToolName` mirror enum resolves without the consumer
544 // having `serde` in their dependency tree under that exact name.
545 pub use serde;
546
547 /// `schemars::schema_for` shim for `#[tool(schema = "derive")]`.
548 ///
549 /// Only present under the `macros-schema` feature; the macro emits a
550 /// `compile_error!` (not a missing-path error) when the feature is off, so
551 /// this absence is never the diagnostic a user sees.
552 #[cfg(feature = "macros-schema")]
553 #[must_use]
554 pub fn schema_for<T: schemars::JsonSchema>() -> schemars::Schema {
555 schemars::schema_for!(T)
556 }
557}
558
559/// Define a tool inline, expanding to a fresh zero-sized struct plus a
560/// [`SimpleTool`] impl — the lowest-ceremony way to add a one-off tool in an
561/// example, test, or script.
562///
563/// This is the declarative counterpart to [`derive@Tool`]: use the derive when
564/// you want a named, reusable tool type; reach for `tool!` when you just need a
565/// closure-like tool right where you register it.
566///
567/// The application context type defaults to `()`; pass `context: MyCtx,` before
568/// the closure to use a different one. The closure receives
569/// `&ToolContext<Ctx>` and the raw `serde_json::Value` arguments and must
570/// return a future resolving to `anyhow::Result<ToolResult>`.
571///
572/// # Example
573///
574/// ```
575/// use agent_sdk::{tool, ToolResult, ToolRegistry};
576/// use serde_json::json;
577///
578/// let weather = tool! {
579/// name: "get_weather",
580/// description: "Get the current weather for a city",
581/// schema: json!({
582/// "type": "object",
583/// "properties": { "city": { "type": "string" } },
584/// "required": ["city"],
585/// }),
586/// |_ctx, input| async move {
587/// let city = input["city"].as_str().unwrap_or("Unknown");
588/// Ok(ToolResult::success(format!("Weather in {city}: Sunny")))
589/// }
590/// };
591///
592/// let mut registry: ToolRegistry<()> = ToolRegistry::new();
593/// registry.register_simple(weather);
594/// ```
595#[macro_export]
596macro_rules! tool {
597 // Default context = ().
598 (
599 name: $name:expr,
600 description: $description:expr,
601 schema: $schema:expr,
602 | $ctx:ident , $input:ident | $body:expr $(,)?
603 ) => {
604 $crate::tool! {
605 name: $name,
606 description: $description,
607 schema: $schema,
608 context: (),
609 |$ctx, $input| $body
610 }
611 };
612 // Explicit context type.
613 (
614 name: $name:expr,
615 description: $description:expr,
616 schema: $schema:expr,
617 context: $ctxty:ty,
618 | $ctx:ident , $input:ident | $body:expr $(,)?
619 ) => {{
620 struct __InlineTool;
621
622 impl $crate::SimpleTool<$ctxty> for __InlineTool {
623 fn name(&self) -> &'static str {
624 $name
625 }
626
627 fn description(&self) -> &'static str {
628 $description
629 }
630
631 fn input_schema(&self) -> $crate::__macro_support::Value {
632 $schema
633 }
634
635 fn execute(
636 &self,
637 $ctx: &$crate::ToolContext<$ctxty>,
638 $input: $crate::__macro_support::Value,
639 ) -> impl ::core::future::Future<
640 Output = $crate::__macro_support::Result<$crate::ToolResult>,
641 > + ::core::marker::Send {
642 $body
643 }
644 }
645
646 __InlineTool
647 }};
648}
649
650// agent-sdk-providers (via thin modules)
651pub use llm::{ContentBlock, ContentSource, Effort, LlmProvider, ThinkingConfig, ThinkingMode};
652pub use model_capabilities::{
653 ModelCapabilities, PricePoint, Pricing, SourceStatus, get_model_capabilities,
654 supported_model_capabilities,
655};
656
657// Schema-validated structured output (Phase 13): the [`ResponseFormat`] request
658// field, the bounded re-prompt runner, and its typed result/error.
659pub use agent_sdk_foundation::llm::ResponseFormat;
660pub use agent_sdk_providers::{
661 StructuredConfig, StructuredOutput, StructuredOutputError, StructuredOutputSupport,
662 run_structured,
663};
664
665// ── Advanced / server-internal contract types ───────────────────────
666/// Server- and host-facing contract types.
667///
668/// These types are not needed by a typical in-process agent. They form the
669/// authoritative boundary that `agent-server` / `agent-service-host` build
670/// on: per-turn outcome and summary contracts, the durable continuation
671/// envelope, the audit-record protocol, the listen/erased tool plumbing, and
672/// the worker-context reconstruction factory.
673///
674/// They are grouped here so that `agent_sdk::` autocomplete and the docs.rs
675/// front page stay dominated by the newcomer-facing surface (see
676/// [`prelude`]). Everything here remains a stable, public re-export — moving
677/// a name into `advanced` is a path change, not a removal.
678pub mod advanced {
679 // Per-turn outcome / summary contract and the durable continuation.
680 pub use crate::types::{
681 AgentContinuation, CONTINUATION_VERSION, ContinuationEnvelope, ListenExecutionContext,
682 TurnOutcome, TurnSummary,
683 };
684
685 // Audit-record protocol emitted at every tool-lifecycle transition.
686 pub use agent_sdk_foundation::audit::{
687 AuditProvenance, ToolAuditOutcome, ToolAuditRecord, ToolAuditRecordParams,
688 };
689
690 // Event-sequencing authority used by the server commit path.
691 pub use crate::authority::{EventAuthority, LocalEventAuthority};
692
693 // Worker-context reconstruction for externalized tool runtimes.
694 pub use crate::seed::{ExecutionContextFactory, HostDependencies};
695
696 // Listen/execute tool protocol and the type-erased registry wrappers.
697 pub use crate::tools::{
698 ErasedAsyncTool, ErasedListenTool, ErasedTool, ErasedToolStatus, ListenExecuteTool,
699 ListenStopReason, ListenToolUpdate,
700 };
701}
702
703// ── Prelude ──────────────────────────────────────────────────────────
704/// The common imports for building an in-process agent.
705///
706/// `use agent_sdk::prelude::*;` brings the ~dozen names a newcomer needs:
707/// the [`builder`], configuration and I/O types, the [`Tool`] surface, the
708/// in-memory event store, the cancellation token, and — when the `anthropic`
709/// feature is enabled (the default) — the
710/// [`AnthropicProvider`](crate::providers::AnthropicProvider). Server-only
711/// contract types are intentionally excluded — reach for [`crate::advanced`]
712/// when you need them.
713pub mod prelude {
714 pub use crate::builder;
715 #[cfg(feature = "anthropic")]
716 pub use crate::providers::AnthropicProvider;
717 // `tool!` (declarative) plus the `Tool` / `TypedTool` / `ToolName` derives
718 // ride along on the same-named trait re-exports below — importing the trait
719 // also imports the derive macro (cf. `serde::Serialize`).
720 pub use crate::tool;
721 pub use crate::{
722 AgentConfig, AgentEvent, AgentInput, CancellationToken, DynamicToolName,
723 InMemoryEventStore, SimpleTool, Tool, ToolContext, ToolName, ToolRegistry, ToolResult,
724 ToolTier, TypedTool,
725 };
726}
727
728// Convenience re-exports
729pub use reminders::{
730 ReminderConfig, ReminderTracker, ReminderTrigger, ToolReminder, append_reminder, wrap_reminder,
731};
732pub use subagent::{
733 METADATA_MAX_SUBAGENT_DEPTH, METADATA_SUBAGENT_DEPTH, SubagentConfig, SubagentFactory,
734 SubagentTool,
735};
736pub use todo::{TodoItem, TodoReadTool, TodoState, TodoStatus, TodoWriteTool};
737pub use user_interaction::{
738 AskUserQuestionTool, ConfirmationRequest, ConfirmationResponse, QuestionOption,
739 QuestionRequest, QuestionResponse,
740};
741
742#[cfg(feature = "otel")]
743#[cfg_attr(docsrs, doc(cfg(feature = "otel")))]
744pub use observability::{
745 CaptureDecision, CaptureKind, CaptureResult, ObservabilityStore, PayloadBundle,
746};