mod builder;
mod context;
mod helpers;
#[cfg(feature = "mcp")]
mod mcp;
mod permission;
mod run;
mod streaming;
mod tools;
mod types;
#[cfg(feature = "session")]
mod session;
pub use builder::AgentBuilder;
pub use context::{ContextConfig, ContextError, ContextLoadResult, ContextSource};
pub use types::{
AgentError, AgentResponse, PermissionError, TokenUsageStats, ToolCallInfo, ToolInfo,
DEFAULT_MAX_CONCURRENT_TOOLS, DEFAULT_PERMISSION_TIMEOUT,
};
#[cfg(feature = "session")]
pub use types::SessionInfo;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{mpsc, RwLock};
use crate::conversation::BoxedConversationManager;
use crate::events::{AgentEvent, AgentHook, HookId};
use crate::permission::{AuthorizationResponse, ToolCallAuthorizer};
use crate::provider::ModelProvider;
use crate::tool::DynTool;
use crate::types::Message;
#[cfg(feature = "session")]
use crate::session::SessionStore;
pub struct Agent {
pub(super) provider: Arc<dyn ModelProvider>,
pub(super) system_prompt: Option<String>,
pub(super) max_concurrent_tools: usize,
pub(super) tools: Vec<Box<dyn DynTool>>,
pub(super) hooks: Arc<parking_lot::RwLock<HashMap<HookId, Arc<dyn AgentHook>>>>,
pub(super) next_hook_id: AtomicU64,
pub(super) authorizer: Arc<RwLock<ToolCallAuthorizer>>,
pub(super) authorization_timeout: Duration,
pub(super) pending_authorizations:
Arc<RwLock<HashMap<String, mpsc::Sender<AuthorizationResponse>>>>,
#[cfg(feature = "mcp")]
pub(super) mcp_clients: Vec<Arc<crate::mcp::McpClient>>,
pub(super) conversation_manager: parking_lot::RwLock<BoxedConversationManager>,
#[cfg(feature = "session")]
pub(super) session_store: Option<Arc<dyn SessionStore>>,
pub(super) context_sources: Vec<ContextSource>,
pub(super) context_config: ContextConfig,
pub(super) last_context_result: parking_lot::RwLock<Option<ContextLoadResult>>,
}
impl Agent {
pub fn add_hook(&self, hook: impl AgentHook + 'static) -> HookId {
let id = HookId(self.next_hook_id.fetch_add(1, Ordering::SeqCst));
self.hooks.write().insert(id, Arc::new(hook));
id
}
pub fn remove_hook(&self, id: HookId) -> bool {
self.hooks.write().remove(&id).is_some()
}
pub(crate) fn emit_event(&self, event: AgentEvent) {
let hooks = self.hooks.read();
for hook in hooks.values() {
hook.on_event(&event);
}
}
pub fn model_name(&self) -> &str {
self.provider.name()
}
pub async fn shutdown(&self) {
#[cfg(feature = "mcp")]
for client in &self.mcp_clients {
let _ = client.disconnect().await;
}
}
pub fn get_context_usage(&self) -> crate::conversation::ContextUsage {
let limits = crate::conversation::ContextLimits::new(self.provider.max_context_tokens());
let provider = &self.provider;
let estimate_tokens = |msgs: &[Message]| provider.estimate_message_tokens(msgs);
self.conversation_manager
.read()
.context_usage(limits, &estimate_tokens)
}
pub fn last_context_info(&self) -> Option<ContextLoadResult> {
self.last_context_result.read().clone()
}
}