Skip to main content

agents_kit/
lib.rs

1//! Portable agent runtimes over Codex App Server, OpenCode, and a
2//! provider-backed Custom implementation.
3//!
4//! The public hierarchy is [`Agent`] → [`Workspace`] → [`Conversation`].
5//! Supporting request and response values live in functional modules instead
6//! of being flattened onto the crate root.
7//!
8//! Implement [`AgentBackend`], [`WorkspaceBackend`] and [`ConversationBackend`]
9//! directly to supply a backend hierarchy. Associated children keep caller types
10//! concrete; the supplied opaque handles implement those same contracts.
11//!
12//! Start with [`config`] and [`AgentBuilder`], supplying [`AgentStorage`] at
13//! creation or explicit typed-ID reopen. [`workspace`] defines backend-host cwd,
14//! roots, policy and revision-checked replacement. [`conversation`] and [`prompt`]
15//! describe accepted work; [`notification`] covers durable acknowledgment and
16//! interactions. [`identity`], [`model`], [`tool`] and [`knowledge`] hold passive
17//! portable inputs, while [`error`] describes typed failure and cleanup context.
18//!
19//! Custom retains direct caller `ProviderBackend` and `ToolsetBackend` values,
20//! using each toolset's associated Handler without a Clone requirement. Unary
21//! and streaming prompts share one accepted-work owner. Dropping an observer
22//! neither cancels that work nor releases its admission slot; use abort or the
23//! consuming lifecycle operations and keep the async runtime alive through cleanup.
24//!
25//! # Walkthroughs
26//!
27//! | Goal | Start here |
28//! | --- | --- |
29//! | Build, close and reopen a supplied hierarchy | The local example below; [`AgentBuilder`] |
30//! | Supply an exact native model and effort | [`model`] and [`config`] |
31//! | Observe output, resolve durable interactions and cancel | [`conversation`] |
32//! | Ingest local sources and choose retrieval scope | [`knowledge`] |
33//! | Implement a caller-owned hierarchy | [`AgentBackend`]'s complete composition example |
34//! | Supply concrete Provider and non-Clone Toolset children | [`AgentBuilder::with_provider`] |
35//!
36//! # Local construction, parent close and fresh-handle reopen
37//!
38//! This runs locally with an owned embedded Lance StateStore directory. Provider
39//! construction is passive: submitting a prompt would require the configured
40//! Ollama service and exact model. The directory and all runtime handles belong
41//! to this example. Agent shutdown preserves quiescent durable records; closing
42//! an individual conversation instead permanently terminates that conversation.
43//!
44//! Caller-built Local actions run in the caller process without Kit sandbox
45//! enforcement. Custom therefore requires `FullAccess` and `Enabled` when actions
46//! are configured, with read-write workspace roots; those roots do not confine the actions. This example's
47//! `ping` action performs no filesystem or network I/O.
48//!
49//! ```
50//! use std::{num::NonZeroUsize, time::Duration};
51//! use agents_kit::{AgentBackend, AgentBuilder, AgentStorage, ConversationBackend, WorkspaceBackend};
52//! use agents_kit::config::{AgentConfig, CustomAgentConfig, CustomLoopLimits, CustomToolApproval};
53//! use agents_kit::conversation::NewConversation;
54//! use agents_kit::{model::ModelSelector, tool::ToolSelection};
55//! use agents_kit::workspace::{NewWorkspace, WorkspaceContext, WorkspaceDefaults,
56//!     WorkspaceExecutionPolicy, WorkspaceNetworkAccess, WorkspaceRoot,
57//!     WorkspaceRootAccess, WorkspaceSandboxMode};
58//! use providers_kit::{Provider, ProviderConfig,
59//!     config::{OllamaConfig, ProviderKind, TransportConfig}};
60//! use storage_kit::{StateStore,
61//!     state::config::{LanceStateStoreConfig, StateStoreConfig}};
62//! use tools_kit::{Toolset, declaration::{ToolInfo, ToolName}, execution::ToolResult,
63//!     local::ToolRegistration, schema::JsonSchema};
64//!
65//! let directory = std::env::temp_dir().join(format!("agent-rustdoc-{}", uuid::Uuid::new_v4()));
66//! std::fs::create_dir(&directory)?;
67//! let store_config = StateStoreConfig::Lance(
68//!     LanceStateStoreConfig::directory(directory.clone()));
69//! let provider_config = ProviderConfig::new(
70//!     ProviderKind::Ollama(OllamaConfig::local()), TransportConfig::default());
71//! let config = AgentConfig::new(
72//!     CustomAgentConfig::new(ModelSelector::new("exact-model")?, ToolSelection::Auto,
73//!         CustomLoopLimits::new(4, 4, 1)?, CustomToolApproval::Deny)?.into(),
74//!     Duration::from_secs(10), NonZeroUsize::MIN)?;
75//! let fresh_tools = || -> Result<Toolset, tools_kit::ToolError> {
76//!     Toolset::local(vec![ToolRegistration::function(
77//!         ToolInfo::new(ToolName::new("ping")?,
78//!             JsonSchema::strict(serde_json::json!({"type": "object"}))),
79//!         |_| async { Ok(ToolResult::text("pong")) },
80//!     )])
81//! };
82//! let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
83//! let outcome = runtime.block_on(async {
84//!     let store = StateStore::connect(store_config.clone()).await?;
85//!     let agent = AgentBuilder::create(config.clone(), AgentStorage::new(store))
86//!         .with_provider(Provider::from_config(provider_config.clone())?)
87//!         .with_toolset(fresh_tools()?).build().await?;
88//!     let agent_id = agent.id();
89//!     let outcome = async {
90//!         let cwd = std::env::current_dir()?;
91//!         let context = WorkspaceContext::new(cwd.clone(),
92//!             vec![WorkspaceRoot::new(cwd, WorkspaceRootAccess::ReadWrite)?],
93//!             WorkspaceExecutionPolicy::new(WorkspaceSandboxMode::FullAccess,
94//!                 WorkspaceNetworkAccess::Enabled))?;
95//!         let workspace = agent.new_workspace(
96//!             NewWorkspace::new("docs", context, WorkspaceDefaults::empty())?).await?;
97//!         let conversation = workspace.new_conversation(
98//!             NewConversation::new(Some("Review".into()), None, Vec::new())?).await?;
99//!         let ids = (workspace.id(), conversation.id());
100//!         assert_eq!(conversation.info().await?.title(), Some("Review"));
101//!         workspace.close().await?; // invalidates runtime children, preserves records
102//!         drop(conversation);
103//!         Ok::<_, Box<dyn std::error::Error>>(ids)
104//!     }.await;
105//!     let shutdown = agent.shutdown().await;
106//!     let (workspace_id, conversation_id) = outcome?;
107//!     shutdown?;
108//!
109//!     // New handles: credentials, tools and storage are supplied again, not recovered.
110//!     let store = StateStore::connect(store_config).await?;
111//!     let reopened = AgentBuilder::reopen(config, AgentStorage::new(store), agent_id)
112//!         .with_provider(Provider::from_config(provider_config)?)
113//!         .with_toolset(fresh_tools()?).build().await?;
114//!     let outcome = async {
115//!         let workspace = reopened.get_workspace(workspace_id).await?;
116//!         let conversation = workspace.get_conversation(conversation_id.clone()).await?;
117//!         assert_eq!(conversation.id(), conversation_id);
118//!         assert_eq!(conversation.info().await?.title(), Some("Review"));
119//!         conversation.close().await?; // terminal conversation cleanup
120//!         workspace.close().await?;
121//!         Ok::<(), Box<dyn std::error::Error>>(())
122//!     }.await;
123//!     let shutdown = reopened.shutdown().await;
124//!     outcome?;
125//!     shutdown?;
126//! # Ok::<(), Box<dyn std::error::Error>>(())
127//! });
128//! // Retain data on failure; successful shutdown is required before deletion.
129//! outcome?;
130//! std::fs::remove_dir_all(directory)?;
131//! # Ok::<(), Box<dyn std::error::Error>>(())
132//! ```
133//!
134//! Native Codex and OpenCode builders can select caller provider and tool types for
135//! static upper composition with [`AgentBuilder::create_for_backends`] or
136//! [`AgentBuilder::reopen_for_backends`]. Those methods retain no provider
137//! value; Custom receives its concrete provider only through
138//! [`AgentBuilder::with_provider`].
139//!
140//! Custom uses the selected Model's associated continuation type directly.
141//! Returned assistant messages remain intact through one prompt's tool turns;
142//! durable history contains only provider-neutral caller and final-assistant
143//! text, so reopen does not reconstruct provider replay state.
144
145mod agent;
146mod agent_storage;
147mod backends;
148mod builder;
149/// Agent construction settings and backend runtime policies.
150pub mod config;
151/// Conversation handles, backend contracts, and conversation requests and responses.
152pub mod conversation;
153mod core;
154pub mod error;
155mod file;
156/// Validated identities and agent inventory information.
157pub mod identity;
158/// Knowledge sources, scopes, ingestion requests, and retrieval policies.
159pub mod knowledge;
160/// Model inventory, opaque selectors, and reasoning settings.
161pub mod model;
162/// Durable notifications, caller resolutions, and notification streams.
163pub mod notification;
164/// Prompt requests, responses, and incremental output streams.
165pub mod prompt;
166/// Portable tool selection and managed tool inventory.
167pub mod tool;
168/// Workspace handles, backend contracts, defaults, and workspace requests.
169pub mod workspace;
170
171pub use crate::agent::{Agent, AgentBackend};
172pub use crate::agent_storage::AgentStorage;
173pub use crate::builder::AgentBuilder;
174pub use crate::conversation::{Conversation, ConversationBackend};
175pub use crate::error::KitError;
176pub use crate::workspace::{Workspace, WorkspaceBackend};
177
178pub(crate) use crate::agent::AgentInfo;
179pub(crate) use crate::config::{
180    AgentConfig, BackendConfig, CodexConfig, CustomAgentConfig, CustomLoopLimits,
181    CustomToolApproval, OpenCodeConfig,
182};
183pub(crate) use crate::conversation::{ConversationInfo, FileRead, NewConversation};
184pub(crate) use crate::core::knowledge::ManagedKnowledge;
185pub(crate) use crate::identity::{AgentId, ConversationId, NotificationId, WorkspaceId};
186pub(crate) use crate::knowledge::{
187    IngestKnowledgeRequest, KnowledgeBudget, KnowledgeContext, KnowledgeOwnerId, KnowledgePolicy,
188    KnowledgeProvenance, KnowledgeRetrievalRequest, KnowledgeScope, KnowledgeSnippet,
189    KnowledgeSource,
190};
191pub(crate) use crate::model::{ModelInfo, ModelSelector, PersonalitySelector, ReasoningEffort};
192pub(crate) use crate::notification::{
193    ConversationNotification, ConversationNotificationKind, ConversationNotificationReceiver,
194    NotificationResolution, ProcessState,
195};
196pub(crate) use crate::prompt::{PromptRequest, PromptResponse, StructuredOutputRequest, UsageInfo};
197pub(crate) use crate::tool::{ToolInfo, ToolSelection};
198pub(crate) use crate::workspace::{
199    DeleteWorkspaceRequest, NewWorkspace, UpdateWorkspaceRequest, WorkspaceContext,
200    WorkspaceDefaults, WorkspaceExecutionPolicy, WorkspaceInfo, WorkspaceNetworkAccess,
201    WorkspaceRoot, WorkspaceRootAccess, WorkspaceSandboxMode, WorkspaceUpdateReadiness,
202};