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