1use serde::Serialize;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Default)]
21pub enum HumanIntervention {
22 #[default]
24 None,
25 Conditional,
29 Required,
31}
32
33#[derive(Debug, Clone, Serialize)]
35pub struct ToolMeta {
36 pub name: &'static str,
38 pub description_key: &'static str,
40 pub category: &'static str,
42 #[serde(default)]
44 pub human_intervention: HumanIntervention,
45}
46
47impl ToolMeta {
48 pub const fn new(
49 name: &'static str,
50 description_key: &'static str,
51 category: &'static str,
52 ) -> Self {
53 Self {
54 name,
55 description_key,
56 category,
57 human_intervention: HumanIntervention::None,
58 }
59 }
60
61 pub const fn with_intervention(mut self, level: HumanIntervention) -> Self {
63 self.human_intervention = level;
64 self
65 }
66}
67
68pub fn known_tools() -> &'static [ToolMeta] {
74 TOOL_CATALOG
75}
76
77const TOOL_CATALOG: &[ToolMeta] = &[
78 ToolMeta::new("read", "tools.read", "fs"),
80 ToolMeta::new("write", "tools.write", "fs").with_intervention(HumanIntervention::Conditional),
81 ToolMeta::new("edit", "tools.edit", "fs").with_intervention(HumanIntervention::Conditional),
82 ToolMeta::new("grep", "tools.grep", "fs"),
83 ToolMeta::new("find", "tools.find", "fs"),
84 ToolMeta::new("ls", "tools.ls", "fs"),
85 ToolMeta::new("web_search", "tools.webSearch", "comms"),
86 ToolMeta::new("get_search_results", "tools.getSearchResults", "comms"),
87 ToolMeta::new("exec", "tools.exec", "exec").with_intervention(HumanIntervention::Required),
89 ToolMeta::new("browse", "tools.browse", "comms"),
90 ToolMeta::new("memory_read", "tools.memoryRead", "memory"),
91 ToolMeta::new("memory_write", "tools.memoryWrite", "memory"),
92 ToolMeta::new("memory_search", "tools.memorySearch", "memory"),
93 ToolMeta::new("project", "tools.project", "system"),
94 ToolMeta::new("kernel_agent", "tools.kernelAgent", "system"),
95 ToolMeta::new("a2a_delegate", "tools.a2aDelegate", "a2a"),
96 ToolMeta::new("a2a_send", "tools.a2aSend", "a2a"),
97 ToolMeta::new("a2a_query", "tools.a2aQuery", "a2a"),
98 ToolMeta::new("persona", "tools.persona", "system"),
99 ToolMeta::new("cron", "tools.cron", "system"),
100 ToolMeta::new("security", "tools.security", "system"),
101 ToolMeta::new("budget", "tools.budget", "system"),
102 ToolMeta::new("resource", "tools.resource", "system"),
103 ToolMeta::new("knowledge", "tools.knowledge", "system"),
104 ToolMeta::new("calendar", "tools.calendar", "system"),
105 ToolMeta::new("send_email", "tools.sendEmail", "comms"),
106];
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn catalog_is_populated() {
114 let tools = known_tools();
115 assert!(!tools.is_empty(), "tool catalog should not be empty");
116 assert!(
117 tools.iter().any(|t| t.name == "read"),
118 "read tool should be in catalog"
119 );
120 assert!(
121 tools.iter().any(|t| t.name == "exec"),
122 "exec tool should be in catalog"
123 );
124 assert!(
125 tools.iter().any(|t| t.name == "memory_read"),
126 "memory_read should be in catalog"
127 );
128 }
129
130 #[test]
131 fn all_tools_have_required_fields() {
132 for tool in known_tools() {
133 assert!(!tool.name.is_empty(), "tool name should not be empty");
134 assert!(
135 !tool.description_key.is_empty(),
136 "description_key should not be empty"
137 );
138 assert!(!tool.category.is_empty(), "category should not be empty");
139 }
140 }
141
142 #[test]
143 fn no_duplicate_names() {
144 let names: Vec<&str> = known_tools().iter().map(|t| t.name).collect();
145 let mut sorted = names.clone();
146 sorted.sort();
147 sorted.dedup();
148 assert_eq!(names.len(), sorted.len(), "duplicate tool names found");
149 }
150}