use std::sync::Arc;
use tokio::sync::RwLock;
use agent_sdk_tools::tools::ToolRegistry;
use crate::primitive_tools::{BashTool, EditTool, GlobTool, GrepTool, ReadTool, WriteTool};
use crate::todo::{TodoReadTool, TodoState, TodoWriteTool};
#[cfg(feature = "web")]
use crate::web::{LinkFetchTool, SearchProvider, WebSearchTool};
use crate::{AgentCapabilities, Environment};
pub struct BuiltinToolsConfig<E: Environment + 'static> {
pub environment: Arc<E>,
pub capabilities: AgentCapabilities,
pub todo_state: Option<Arc<RwLock<TodoState>>>,
#[cfg(feature = "web")]
pub link_fetch: bool,
}
pub fn register_builtin_tools<Ctx, E>(
registry: &mut ToolRegistry<Ctx>,
config: BuiltinToolsConfig<E>,
) where
Ctx: Send + Sync + 'static,
E: Environment + 'static,
{
register_primitives(registry, config.environment, config.capabilities);
if let Some(state) = config.todo_state {
register_todo_tools(registry, state);
}
#[cfg(feature = "web")]
if config.link_fetch {
register_link_fetch(registry);
}
}
pub fn register_primitives<Ctx, E>(
registry: &mut ToolRegistry<Ctx>,
environment: Arc<E>,
capabilities: AgentCapabilities,
) where
Ctx: Send + Sync + 'static,
E: Environment + 'static,
{
registry.register(ReadTool::new(
Arc::clone(&environment),
capabilities.clone(),
));
registry.register(WriteTool::new(
Arc::clone(&environment),
capabilities.clone(),
));
registry.register(EditTool::new(
Arc::clone(&environment),
capabilities.clone(),
));
registry.register(BashTool::new(
Arc::clone(&environment),
capabilities.clone(),
));
registry.register(GlobTool::new(
Arc::clone(&environment),
capabilities.clone(),
));
registry.register(GrepTool::new(environment, capabilities));
}
pub fn register_todo_tools<Ctx>(registry: &mut ToolRegistry<Ctx>, state: Arc<RwLock<TodoState>>)
where
Ctx: Send + Sync + 'static,
{
registry.register(TodoReadTool::new(Arc::clone(&state)));
registry.register(TodoWriteTool::new(state));
}
#[cfg(feature = "web")]
pub fn register_link_fetch<Ctx>(registry: &mut ToolRegistry<Ctx>)
where
Ctx: Send + Sync + 'static,
{
registry.register(LinkFetchTool::new());
}
#[cfg(feature = "web")]
pub fn register_web_search<Ctx, P>(registry: &mut ToolRegistry<Ctx>, provider: P)
where
Ctx: Send + Sync + 'static,
P: SearchProvider + 'static,
{
registry.register(WebSearchTool::new(provider));
}
#[cfg(test)]
mod tests {
use super::*;
use crate::InMemoryFileSystem;
#[cfg(feature = "web")]
#[test]
fn register_builtin_tools_wires_primitives_and_todo_and_fetch() {
let fs = Arc::new(InMemoryFileSystem::new("/workspace"));
let todo = Arc::new(RwLock::new(TodoState::new()));
let mut registry = ToolRegistry::<()>::new();
register_builtin_tools(
&mut registry,
BuiltinToolsConfig {
environment: fs,
capabilities: AgentCapabilities::full_access(),
todo_state: Some(todo),
link_fetch: true,
},
);
for expected in [
"read",
"write",
"edit",
"bash",
"glob",
"grep",
"todo_read",
"todo_write",
"link_fetch",
] {
assert!(
registry.get(expected).is_some(),
"expected '{expected}' registered",
);
}
}
#[test]
fn register_primitives_registers_exactly_six_tools() {
let fs = Arc::new(InMemoryFileSystem::new("/workspace"));
let mut registry = ToolRegistry::<()>::new();
register_primitives(&mut registry, fs, AgentCapabilities::read_only());
for expected in ["read", "write", "edit", "bash", "glob", "grep"] {
assert!(
registry.get(expected).is_some(),
"expected '{expected}' registered",
);
}
}
#[test]
fn builtin_tools_config_skips_optional_families_when_unset() {
let fs = Arc::new(InMemoryFileSystem::new("/workspace"));
let mut registry = ToolRegistry::<()>::new();
register_builtin_tools(
&mut registry,
BuiltinToolsConfig {
environment: fs,
capabilities: AgentCapabilities::full_access(),
todo_state: None,
#[cfg(feature = "web")]
link_fetch: false,
},
);
assert!(registry.get("read").is_some());
assert!(registry.get("todo_read").is_none());
assert!(registry.get("link_fetch").is_none());
}
}