1pub mod builtin;
4mod condition;
5pub mod mcp;
6mod provider;
7mod registry;
8pub mod security;
9mod types;
10
11pub use ai_agents_core::{
12 CommandBindingKind, CommandPolicyBinding, DomainPolicyBinding, PathAccessMode, PathBindingKind,
13 PathPolicyBinding, PermissionOutcome, ResultLimitBinding, ResultLimitKind, Tool,
14 ToolActorContext, ToolApprovalRecord, ToolApprovalStatus, ToolCallClassification,
15 ToolCallSource, ToolCancellationToken, ToolExecutionContext, ToolExecutionLimits,
16 ToolExecutionRecord, ToolExecutionRequest, ToolInfo, ToolInvoker, ToolOperationKind,
17 ToolPolicyBindings, ToolPolicyDecisionRecord, ToolResult, ToolSafetyMetadata,
18 ToolSideEffectLevel,
19};
20pub use condition::{
21 ConditionEvaluator, EvaluationContext, LLMGetter, SimpleLLMGetter, ToolCallRecord,
22};
23pub use provider::{ProviderHealth, ToolDescriptor, ToolProvider, ToolProviderError};
24pub use registry::{ResolvedTool, ToolIdentity, ToolRegistry, ToolSchemaPromptMode};
25pub use types::{
26 CommandRequest, CommandResponse, CommandRunner, CommandRunnerSlot, DiagnosticItem,
27 DiagnosticSeverity, DiagnosticsProvider, DiagnosticsProviderSlot, DiagnosticsRequest,
28 DiagnosticsResponse, FileVersionEvidence, FileVersionStore, ProcessCommandRunner,
29 QuestionHandler, QuestionHandlerSlot, QuestionRequest, QuestionResponse, StaticCommandRunner,
30 StaticDiagnosticsProvider, StaticWebSearchProvider, TodoItem, TodoStatus, TodoStore,
31 ToolAliases, ToolContext, ToolMetadata, ToolProviderType, TrustLevel, UnavailableCommandRunner,
32 UnavailableDiagnosticsProvider, UnavailableWebSearchProvider, WebSearchProvider,
33 WebSearchProviderSlot, WebSearchRequest, WebSearchResponse, WebSearchResultItem,
34 WebSearchSafeSearch, file_version_evidence,
35};
36
37pub use builtin::HttpTool;
38pub use builtin::{
39 AskUserTool, CalculatorTool, CommandTool, CopyPathTool, DateTimeTool, DeletePathTool,
40 DiagnosticsTool, EchoTool, FileEditTool, FileInfoTool, FileListTool, FileReadTool, FileTool,
41 FileWriteTool, GitDiffTool, GitStatusTool, GlobTool, GrepTool, JsonTool, MathTool,
42 MovePathTool, PatchTool, RandomTool, SleepTool, TemplateTool, TextTool, TodoTool,
43 WebFetchResolver, WebFetchTool, WebFetchTransport, WebFetchTransportRequest,
44 WebFetchTransportResponse, WebSearchTool,
45};
46
47pub use security::{
48 CommandPolicyConfig, CommandRuleConfig, CommandTemplateConfig, DomainPolicyConfig,
49 MAX_TOOL_TIMEOUT_MS, NoWritePolicyBehavior, OperationPolicyConfig, PathPolicyConfig,
50 SecurityCheckResult, ToolPolicyConfig, ToolSecurityConfig, ToolSecurityEngine,
51};
52
53use schemars::JsonSchema;
54use serde::Deserialize;
55use std::sync::Arc;
56use thiserror::Error;
57
58#[derive(Debug, Error)]
59pub enum ToolError {
60 #[error("Tool not found: {0}")]
61 NotFound(String),
62 #[error("Tool already registered: {0}")]
63 AlreadyRegistered(String),
64 #[error("Duplicate: {0}")]
65 Duplicate(String),
66 #[error("Tool execution failed: {0}")]
67 ExecutionFailed(String),
68 #[error("Invalid arguments: {0}")]
69 InvalidArguments(String),
70 #[error("Provider error: {0}")]
71 Provider(String),
72}
73
74pub fn generate_schema<T: JsonSchema>() -> serde_json::Value {
75 let schema = schemars::schema_for!(T);
76 serde_json::to_value(schema).unwrap_or_else(|_| serde_json::json!({}))
77}
78
79pub(crate) fn deserialize_optional_positive_usize<'de, D>(
80 deserializer: D,
81) -> std::result::Result<Option<usize>, D::Error>
82where
83 D: serde::Deserializer<'de>,
84{
85 let value = Option::<usize>::deserialize(deserializer)?;
86 validate_positive_max_results(value).map_err(serde::de::Error::custom)?;
87 Ok(value)
88}
89
90pub(crate) fn validate_positive_max_results(
91 value: Option<usize>,
92) -> std::result::Result<(), &'static str> {
93 if value == Some(0) {
94 return Err("max_results must be greater than 0");
95 }
96 Ok(())
97}
98
99pub fn create_builtin_registry() -> ToolRegistry {
100 let mut registry = ToolRegistry::new();
101 registry
102 .register(Arc::new(CalculatorTool::new()))
103 .expect("failed to register calculator");
104 registry
105 .register(Arc::new(EchoTool::new()))
106 .expect("failed to register echo");
107 registry
108 .register(Arc::new(DateTimeTool::new()))
109 .expect("failed to register datetime");
110 registry
111 .register(Arc::new(JsonTool::new()))
112 .expect("failed to register json");
113 registry
114 .register(Arc::new(RandomTool::new()))
115 .expect("failed to register random");
116 registry
117 .register(Arc::new(FileTool::new()))
118 .expect("failed to register file");
119 registry
120 .register(Arc::new(GlobTool::new()))
121 .expect("failed to register glob");
122 registry
123 .register(Arc::new(GrepTool::new()))
124 .expect("failed to register grep");
125 let file_versions = registry.file_version_store();
126 registry
127 .register(Arc::new(FileReadTool::with_version_store(
128 file_versions.clone(),
129 )))
130 .expect("failed to register file_read");
131 registry
132 .register(Arc::new(FileWriteTool::with_version_store(
133 file_versions.clone(),
134 )))
135 .expect("failed to register file_write");
136 registry
137 .register(Arc::new(FileEditTool::with_version_store(
138 file_versions.clone(),
139 )))
140 .expect("failed to register file_edit");
141 registry
142 .register(Arc::new(PatchTool::with_version_store(file_versions)))
143 .expect("failed to register patch");
144 registry
145 .register(Arc::new(CopyPathTool::new()))
146 .expect("failed to register copy_path");
147 registry
148 .register(Arc::new(MovePathTool::new()))
149 .expect("failed to register move_path");
150 registry
151 .register(Arc::new(DeletePathTool::new()))
152 .expect("failed to register delete_path");
153 registry
154 .register(Arc::new(FileListTool::new()))
155 .expect("failed to register file_list");
156 registry
157 .register(Arc::new(FileInfoTool::new()))
158 .expect("failed to register file_info");
159 registry
160 .register(Arc::new(GitStatusTool::new()))
161 .expect("failed to register git_status");
162 registry
163 .register(Arc::new(GitDiffTool::new()))
164 .expect("failed to register git_diff");
165 registry
166 .register(Arc::new(DiagnosticsTool::new(
167 registry.diagnostics_provider_slot(),
168 )))
169 .expect("failed to register diagnostics");
170 registry
171 .register(Arc::new(AskUserTool::new(registry.question_handler_slot())))
172 .expect("failed to register ask_user");
173 registry
174 .register(Arc::new(TodoTool::new(registry.todo_store())))
175 .expect("failed to register todo");
176 registry
177 .register(Arc::new(SleepTool::new()))
178 .expect("failed to register sleep");
179 registry
180 .register(Arc::new(WebFetchTool::with_extractor_slot(
181 registry.web_fetch_extractor_slot(),
182 )))
183 .expect("failed to register web_fetch");
184 registry
185 .register(Arc::new(WebSearchTool::with_provider_slot(
186 registry.web_search_provider_slot(),
187 )))
188 .expect("failed to register web_search");
189 registry
190 .register(Arc::new(CommandTool::new(registry.command_runner_slot())))
191 .expect("failed to register command");
192 registry
193 .register(Arc::new(TextTool::new()))
194 .expect("failed to register text");
195 registry
196 .register(Arc::new(TemplateTool::new()))
197 .expect("failed to register template");
198 registry
199 .register(Arc::new(MathTool::new()))
200 .expect("failed to register math");
201 registry
202 .register(Arc::new(HttpTool::new()))
203 .expect("failed to register http");
204 registry
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn builtin_max_results_schemas_require_positive_values() {
213 let registry = create_builtin_registry();
214 for tool_id in [
215 "glob",
216 "grep",
217 "file_list",
218 "git_status",
219 "diagnostics",
220 "web_search",
221 ] {
222 let schema = registry.get(tool_id).unwrap().input_schema();
223 assert_eq!(
224 schema["properties"]["max_results"]["minimum"],
225 serde_json::json!(1),
226 "{tool_id} must advertise positive max_results"
227 );
228 }
229 }
230}