1use std::collections::HashMap;
2use std::future::Future;
3use std::pin::Pin;
4
5use tokio_util::sync::CancellationToken;
6
7use crate::error::RuntimeError;
8use crate::value::Value;
9
10pub type BoxFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
15
16pub type ToolResult = Result<Value, RuntimeError>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Tier {
20 Zero,
21 One,
22 Two,
23 Three,
24 Four,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
28pub enum ApprovalLevel {
29 Auto,
30 Approve,
31 Dangerous,
32}
33
34impl ApprovalLevel {
35 pub fn from_tier(tier: Tier) -> Self {
36 match tier {
37 Tier::Zero => ApprovalLevel::Auto,
38 Tier::One | Tier::Two => ApprovalLevel::Approve,
39 Tier::Three | Tier::Four => ApprovalLevel::Dangerous,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum CancelBehavior {
46 AbortSafe,
47 Revertible,
48 Atomic,
49 Irreversible,
50}
51
52#[derive(Debug, Default, Clone)]
53pub struct ToolArgs {
54 pub positional: Vec<Value>,
55 pub named: Vec<(String, Value)>,
56}
57
58impl ToolArgs {
59 pub fn positional(&self, index: usize) -> Result<&Value, RuntimeError> {
60 self.positional
61 .get(index)
62 .ok_or_else(|| RuntimeError::MissingArg(format!("positional[{index}]")))
63 }
64
65 pub fn named(&self, name: &str) -> Option<&Value> {
66 self.named.iter().find(|(k, _)| k == name).map(|(_, v)| v)
67 }
68}
69
70#[derive(Clone, Default)]
71pub struct ToolCtx {
72 pub cancel: CancellationToken,
73 pub turn_id: Option<crate::event::TurnId>,
74 pub flow_run_id: Option<crate::event::FlowRunId>,
75 pub event_seq: Option<u64>,
76 pub prompt_resolver: Option<std::sync::Arc<dyn crate::rendezvous::PromptResolver>>,
77 pub registry: Option<std::sync::Arc<ToolRegistry>>,
78 pub sandbox: Option<std::sync::Arc<dyn crate::sandbox::Sandbox>>,
79 pub events: Option<crate::event::EventSink>,
80 pub stdout_broadcast: Option<tokio::sync::broadcast::Sender<String>>,
81 pub session_messages: Option<std::sync::Arc<Vec<crate::message::Message>>>,
82 pub session_messages_handle:
83 Option<std::sync::Arc<std::sync::Mutex<Vec<crate::message::Message>>>>,
84 pub session_runtime: Option<std::sync::Arc<crate::session::Session>>,
85 pub compact_lock_handle: Option<std::sync::Arc<tokio::sync::Mutex<()>>>,
86 pub current_node_id: Option<String>,
87 pub stream_tx: Option<tokio::sync::broadcast::Sender<crate::stream::StreamFrame>>,
88 pub read_files:
89 Option<std::sync::Arc<std::sync::Mutex<std::collections::HashSet<std::path::PathBuf>>>>,
90 pub approval: Option<std::sync::Arc<crate::session::ApprovalRegistry>>,
91 pub forms: Option<std::sync::Arc<crate::session::FormRegistry>>,
92 pub providers: Option<std::sync::Arc<crate::provider::ProviderRegistry>>,
93 pub session_dir: Option<std::path::PathBuf>,
94 pub data_root: Option<std::path::PathBuf>,
95 pub project_index: Option<std::sync::Arc<crate::index::AnchorIndex>>,
96 pub fs_access: crate::fs_access::FsAccessPolicy,
97 pub lifecycle_fire_tx:
98 Option<tokio::sync::mpsc::UnboundedSender<atman_dsl::ast::LifecycleEvent>>,
99 pub bg_registry: Option<std::sync::Arc<crate::tools::bash_bg::BgRegistry>>,
100 pub term_registry: Option<std::sync::Arc<crate::tools::term::TermRegistry>>,
101 pub watch_hub: Option<std::sync::Arc<crate::watch::WatchHub>>,
102 pub flow_registry: Option<std::sync::Arc<crate::tools::agent_ctrl::FlowRegistry>>,
103 pub task_registry: Option<crate::task_registry::TaskRegistry>,
104 pub session_id: Option<String>,
105 pub trust: Option<crate::trust::TrustConfig>,
106 pub safety: Option<crate::safety::SafetyConfig>,
107 pub current_model: Option<String>,
108 pub watch_rules: Option<crate::streaming::WatchRules>,
109 pub on_memory_recent: Option<std::sync::Arc<dyn Fn(u16) + Send + Sync>>,
111 pub history_store: Option<std::sync::Arc<dyn crate::history_store::HistoryStore>>,
112 pub agent_entry: Option<std::sync::Arc<crate::tools::agent_ctrl::FlowEntry>>,
113}
114
115impl ToolCtx {
116 pub fn new() -> Self {
117 Self::default()
118 }
119
120 pub fn with_anchors(
121 mut self,
122 turn_id: Option<crate::event::TurnId>,
123 flow_run_id: Option<crate::event::FlowRunId>,
124 event_seq: Option<u64>,
125 ) -> Self {
126 self.turn_id = turn_id;
127 self.flow_run_id = flow_run_id;
128 self.event_seq = event_seq;
129 self
130 }
131
132 pub fn with_registry(mut self, registry: std::sync::Arc<ToolRegistry>) -> Self {
133 self.registry = Some(registry);
134 self
135 }
136
137 pub fn with_sandbox(mut self, sandbox: std::sync::Arc<dyn crate::sandbox::Sandbox>) -> Self {
138 self.sandbox = Some(sandbox);
139 self
140 }
141
142 pub fn with_events(mut self, events: crate::event::EventSink) -> Self {
143 self.events = Some(events);
144 self
145 }
146
147 pub fn with_stdout_broadcast(mut self, tx: tokio::sync::broadcast::Sender<String>) -> Self {
148 self.stdout_broadcast = Some(tx);
149 self
150 }
151
152 pub fn with_session_messages(
153 mut self,
154 msgs: std::sync::Arc<Vec<crate::message::Message>>,
155 ) -> Self {
156 self.session_messages = Some(msgs);
157 self
158 }
159
160 pub fn with_session_messages_handle(
161 mut self,
162 handle: std::sync::Arc<std::sync::Mutex<Vec<crate::message::Message>>>,
163 ) -> Self {
164 self.session_messages_handle = Some(handle);
165 self
166 }
167
168 pub fn with_session_runtime(
169 mut self,
170 session: std::sync::Arc<crate::session::Session>,
171 ) -> Self {
172 self.session_runtime = Some(session);
173 self
174 }
175
176 pub fn with_compact_lock_handle(
177 mut self,
178 handle: std::sync::Arc<tokio::sync::Mutex<()>>,
179 ) -> Self {
180 self.compact_lock_handle = Some(handle);
181 self
182 }
183
184 pub fn with_current_node(mut self, node_id: Option<String>) -> Self {
185 self.current_node_id = node_id;
186 self
187 }
188
189 pub fn with_read_files(
190 mut self,
191 set: std::sync::Arc<std::sync::Mutex<std::collections::HashSet<std::path::PathBuf>>>,
192 ) -> Self {
193 self.read_files = Some(set);
194 self
195 }
196
197 pub fn with_providers(
198 mut self,
199 providers: std::sync::Arc<crate::provider::ProviderRegistry>,
200 ) -> Self {
201 self.providers = Some(providers);
202 self
203 }
204
205 pub fn with_session_dir(mut self, dir: std::path::PathBuf) -> Self {
206 self.session_dir = Some(dir);
207 self
208 }
209
210 pub fn with_data_root(mut self, dir: std::path::PathBuf) -> Self {
211 self.data_root = Some(dir);
212 self
213 }
214
215 pub fn with_approval(
216 mut self,
217 approval: std::sync::Arc<crate::session::ApprovalRegistry>,
218 ) -> Self {
219 self.approval = Some(approval);
220 self
221 }
222
223 pub fn with_fs_access(mut self, policy: crate::fs_access::FsAccessPolicy) -> Self {
224 self.fs_access = policy;
225 self
226 }
227
228 pub fn with_lifecycle_fire_tx(
229 mut self,
230 tx: tokio::sync::mpsc::UnboundedSender<atman_dsl::ast::LifecycleEvent>,
231 ) -> Self {
232 self.lifecycle_fire_tx = Some(tx);
233 self
234 }
235
236 pub fn with_forms(mut self, forms: std::sync::Arc<crate::session::FormRegistry>) -> Self {
237 self.forms = Some(forms);
238 self
239 }
240
241 pub fn with_bg_registry(
242 mut self,
243 registry: std::sync::Arc<crate::tools::bash_bg::BgRegistry>,
244 ) -> Self {
245 self.bg_registry = Some(registry);
246 self
247 }
248
249 pub fn with_session_id(mut self, id: impl Into<String>) -> Self {
250 self.session_id = Some(id.into());
251 self
252 }
253
254 pub fn with_trust(mut self, trust: crate::trust::TrustConfig) -> Self {
255 self.trust = Some(trust);
256 self
257 }
258
259 pub fn with_safety(mut self, safety: crate::safety::SafetyConfig) -> Self {
260 self.safety = Some(safety);
261 self
262 }
263
264 pub fn with_current_model(mut self, model: impl Into<String>) -> Self {
265 self.current_model = Some(model.into());
266 self
267 }
268
269 pub fn with_watch_rules(mut self, rules: crate::streaming::WatchRules) -> Self {
270 self.watch_rules = Some(rules);
271 self
272 }
273
274 pub fn with_term_registry(
275 mut self,
276 registry: std::sync::Arc<crate::tools::term::TermRegistry>,
277 ) -> Self {
278 self.term_registry = Some(registry);
279 self
280 }
281
282 pub fn with_watch_hub(mut self, hub: std::sync::Arc<crate::watch::WatchHub>) -> Self {
283 self.watch_hub = Some(hub);
284 self
285 }
286
287 pub fn with_flow_registry(
288 mut self,
289 registry: std::sync::Arc<crate::tools::agent_ctrl::FlowRegistry>,
290 ) -> Self {
291 self.flow_registry = Some(registry);
292 self
293 }
294
295 pub fn with_task_registry(mut self, registry: crate::task_registry::TaskRegistry) -> Self {
296 self.task_registry = Some(registry);
297 self
298 }
299
300 pub fn note_read(&self, path: &std::path::Path) {
301 if let Some(set) = &self.read_files
302 && let Ok(mut lock) = set.lock()
303 {
304 lock.insert(path.to_path_buf());
305 }
306 }
307
308 pub fn has_read(&self, path: &std::path::Path) -> bool {
309 self.read_files
310 .as_ref()
311 .and_then(|set| set.lock().ok().map(|lock| lock.contains(path)))
312 .unwrap_or(false)
313 }
314
315 pub fn with_project_index(mut self, idx: std::sync::Arc<crate::index::AnchorIndex>) -> Self {
316 self.project_index = Some(idx);
317 self
318 }
319
320 pub fn with_history_store(
321 mut self,
322 store: std::sync::Arc<dyn crate::history_store::HistoryStore>,
323 ) -> Self {
324 self.history_store = Some(store);
325 self
326 }
327
328 pub fn with_agent_entry(
329 mut self,
330 entry: std::sync::Arc<crate::tools::agent_ctrl::FlowEntry>,
331 ) -> Self {
332 self.agent_entry = Some(entry);
333 self
334 }
335
336 pub fn with_stream_tx(
337 mut self,
338 tx: tokio::sync::broadcast::Sender<crate::stream::StreamFrame>,
339 ) -> Self {
340 self.stream_tx = Some(tx);
341 self
342 }
343}
344
345pub trait Tool: Send + Sync {
346 fn name(&self) -> &str;
347 fn tier(&self) -> Tier;
348 fn approval_level(&self, _args: &ToolArgs, _ctx: &ToolCtx) -> ApprovalLevel {
349 ApprovalLevel::from_tier(self.tier())
350 }
351 fn cancel_behavior(&self) -> CancelBehavior {
352 CancelBehavior::AbortSafe
353 }
354 fn description(&self) -> Option<&str> {
355 None
356 }
357 fn input_schema(&self) -> serde_json::Value {
358 serde_json::json!({"type": "object"})
359 }
360 fn call<'a>(&'a self, args: ToolArgs, ctx: &'a ToolCtx) -> BoxFut<'a, ToolResult>;
361 fn preview_call<'a>(
362 &'a self,
363 _args: &'a ToolArgs,
364 _ctx: &'a ToolCtx,
365 ) -> BoxFut<'a, Option<String>> {
366 Box::pin(async { None })
367 }
368}
369
370pub fn tool_spec(tool: &dyn Tool) -> ToolSpec {
371 ToolSpec {
372 name: tool.name().to_string(),
373 description: tool.description().map(str::to_string),
374 input_schema: tool.input_schema(),
375 }
376}
377
378#[derive(Debug, Clone, serde::Serialize)]
379pub struct ToolSpec {
380 pub name: String,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub description: Option<String>,
383 pub input_schema: serde_json::Value,
384}
385
386#[derive(Default, Clone)]
387pub struct ToolRegistry {
388 tools: std::sync::Arc<std::sync::RwLock<HashMap<String, std::sync::Arc<dyn Tool>>>>,
389}
390
391impl ToolRegistry {
392 pub fn new() -> Self {
393 Self::default()
394 }
395
396 pub fn register(&self, tool: std::sync::Arc<dyn Tool>) {
397 self.tools
398 .write()
399 .unwrap()
400 .insert(tool.name().to_string(), tool);
401 }
402
403 pub fn get(&self, name: &str) -> Option<std::sync::Arc<dyn Tool>> {
404 self.tools.read().unwrap().get(name).cloned()
405 }
406
407 pub fn has(&self, name: &str) -> bool {
408 self.tools.read().unwrap().contains_key(name)
409 }
410
411 pub fn names(&self) -> Vec<String> {
412 self.tools.read().unwrap().keys().cloned().collect()
413 }
414
415 pub fn iter(&self) -> Vec<(String, std::sync::Arc<dyn Tool>)> {
416 self.tools
417 .read()
418 .unwrap()
419 .iter()
420 .map(|(k, v)| (k.clone(), v.clone()))
421 .collect()
422 }
423
424 pub fn unregister_prefix(&self, prefix: &str) {
426 self.tools
427 .write()
428 .unwrap()
429 .retain(|k, _| !k.starts_with(prefix));
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn approval_level_default_maps_from_tier() {
439 assert_eq!(ApprovalLevel::from_tier(Tier::Zero), ApprovalLevel::Auto);
440 assert_eq!(ApprovalLevel::from_tier(Tier::One), ApprovalLevel::Approve);
441 assert_eq!(ApprovalLevel::from_tier(Tier::Two), ApprovalLevel::Approve);
442 assert_eq!(
443 ApprovalLevel::from_tier(Tier::Three),
444 ApprovalLevel::Dangerous
445 );
446 assert_eq!(
447 ApprovalLevel::from_tier(Tier::Four),
448 ApprovalLevel::Dangerous
449 );
450 }
451
452 #[test]
453 fn approval_level_ordered_auto_lt_approve_lt_dangerous() {
454 assert!(ApprovalLevel::Auto < ApprovalLevel::Approve);
455 assert!(ApprovalLevel::Approve < ApprovalLevel::Dangerous);
456 }
457}