perspt_agent/tools/handlers/
mod.rs1mod deps;
12mod exec;
13pub mod external;
14mod fs;
15mod lsp;
16mod verify;
17
18use std::collections::BTreeMap;
19use std::sync::Arc;
20
21use anyhow::Result;
22use perspt_sdk::ToolEntry;
23
24use crate::candidate::CandidateWorkspace;
25use crate::toolloop::EffectOutcome;
26
27pub(crate) use lsp::LspSessions;
28
29#[async_trait::async_trait]
33pub trait CandidateToolHandler: Send + Sync {
34 async fn apply(
35 &self,
36 workspace: &CandidateWorkspace,
37 call: &perspt_sdk::ProviderToolCall,
38 entry: &ToolEntry,
39 ) -> Result<EffectOutcome>;
40}
41
42pub struct CandidateHandlerRegistry {
45 handlers: BTreeMap<String, Arc<dyn CandidateToolHandler>>,
46 fallback: Option<Arc<dyn CandidateToolHandler>>,
49}
50
51impl std::fmt::Debug for CandidateHandlerRegistry {
52 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 formatter
54 .debug_struct("CandidateHandlerRegistry")
55 .field("handlers", &self.handlers.keys().collect::<Vec<_>>())
56 .finish()
57 }
58}
59
60impl CandidateHandlerRegistry {
61 pub fn empty() -> Self {
63 Self {
64 handlers: BTreeMap::new(),
65 fallback: None,
66 }
67 }
68
69 pub fn with_builtins() -> Self {
72 let mut registry = Self::empty();
73 fs::register_workspace_ops(&mut registry);
74 verify::register(&mut registry);
75 exec::register(&mut registry);
76 lsp::register(&mut registry);
77 deps::register(&mut registry);
78 registry
79 }
80
81 pub fn register(
82 &mut self,
83 name: impl Into<String>,
84 handler: Arc<dyn CandidateToolHandler>,
85 ) -> Result<()> {
86 let name = name.into();
87 anyhow::ensure!(
88 !self.handlers.contains_key(&name),
89 "duplicate tool handler registration: {name}"
90 );
91 self.handlers.insert(name, handler);
92 Ok(())
93 }
94
95 pub fn get(&self, name: &str) -> Option<&Arc<dyn CandidateToolHandler>> {
96 self.handlers.get(name)
97 }
98
99 pub fn set_fallback(&mut self, handler: Arc<dyn CandidateToolHandler>) {
101 self.fallback = Some(handler);
102 }
103
104 pub fn has_fallback(&self) -> bool {
105 self.fallback.is_some()
106 }
107
108 pub fn resolve(&self, name: &str) -> Option<&Arc<dyn CandidateToolHandler>> {
110 self.handlers.get(name).or(self.fallback.as_ref())
111 }
112
113 pub fn names(&self) -> impl Iterator<Item = &str> {
114 self.handlers.keys().map(String::as_str)
115 }
116}
117
118impl Default for CandidateHandlerRegistry {
119 fn default() -> Self {
120 Self::with_builtins()
121 }
122}