Skip to main content

bamboo_domain/
tool_names.rs

1//! Tool name constants and normalization functions.
2//!
3//! These are pure data and functions with zero crate dependencies. They are
4//! used by both `core::config` and `agent::tools::executor`.
5
6/// List of all built-in tool names.
7///
8/// This list intentionally includes only tools that are always registered by
9/// `BuiltinToolExecutor::new()`. Optional tools (for example integrations that
10/// depend on host binaries) should NOT be added here.
11pub const BUILTIN_TOOL_NAMES: [&str; 21] = [
12    "conclusion_with_options",
13    "Bash",
14    "BashOutput",
15    "Edit",
16    "EnterPlanMode",
17    "ExitPlanMode",
18    "GetFileInfo",
19    "Glob",
20    "Grep",
21    "js_repl",
22    "KillShell",
23    "session_note",
24    "NotebookEdit",
25    "Read",
26    "request_permissions",
27    "Sleep",
28    "Task",
29    "WebFetch",
30    "WebSearch",
31    "Workspace",
32    "Write",
33];
34
35/// Tool names that are accepted as aliases for built-in/server tools but are not
36/// independently listed in `BUILTIN_TOOL_NAMES`/`SERVER_TOOL_NAMES`. Calls to these names are
37/// transparently routed to their canonical counterpart.
38pub const BUILTIN_TOOL_ALIASES: [(&str, &str); 9] = [
39    // apply_patch is a patch-only alias for Edit
40    ("apply_patch", "Edit"),
41    // FileExists is subsumed by GetFileInfo (returns {exists: false} for missing paths)
42    ("FileExists", "GetFileInfo"),
43    // GetCurrentDir + SetWorkspace are subsumed by Workspace
44    ("GetCurrentDir", "Workspace"),
45    ("SetWorkspace", "Workspace"),
46    // Session note rename
47    ("memory_note", "session_note"),
48    // Server tool renames: `recall` was ambiguous with durable-memory recall
49    // (RAG-style relevant-memory injection). The canonical name is now
50    // `session_history` — strictly a read-only viewer over local SQLite
51    // session storage. Older names still route to the canonical tool.
52    ("recall", "session_history"),
53    ("session_inspector", "session_history"),
54    ("schedule_tasks", "scheduler"),
55    // SubSession manager alias
56    ("sub_session_manager", "SubSession"),
57];
58
59pub const SERVER_TOOL_NAMES: [&str; 7] = [
60    "SubSession",
61    "compact_context",
62    "scheduler",
63    "session_history",
64    "memory",
65    "load_skill",
66    "read_skill_resource",
67];
68
69/// Normalizes a tool reference to a standard tool name.
70///
71/// Returns None if the tool name is not recognized.
72pub fn normalize_tool_ref(value: &str) -> Option<String> {
73    let trimmed = value.trim();
74    if trimmed.is_empty() {
75        return None;
76    }
77    let raw_tool_name = trimmed.split("::").last().unwrap_or(trimmed);
78    let normalized = normalize_builtin_alias(raw_tool_name);
79
80    // Check canonical tool names first
81    if let Some(name) = BUILTIN_TOOL_NAMES
82        .iter()
83        .chain(SERVER_TOOL_NAMES.iter())
84        .find(|name| name.eq_ignore_ascii_case(normalized))
85    {
86        return Some((*name).to_string());
87    }
88
89    // Check aliases – return the *alias* name so callers see what was typed,
90    // while the executor routes it to the canonical tool.
91    BUILTIN_TOOL_ALIASES
92        .iter()
93        .find(|(alias, _)| alias.eq_ignore_ascii_case(normalized))
94        .map(|(alias, _)| (*alias).to_string())
95}
96
97/// Returns the canonical tool name for an alias, or `None` if the name is
98/// not an alias.
99pub fn resolve_alias(name: &str) -> Option<&'static str> {
100    BUILTIN_TOOL_ALIASES
101        .iter()
102        .find(|(alias, _)| alias.eq_ignore_ascii_case(name))
103        .map(|(_, canonical)| *canonical)
104}
105
106pub fn normalize_builtin_alias(name: &str) -> &str {
107    match name {
108        // Backward compatibility for earlier camelCase and snake_case names.
109        "conclusionWithOptions" => "conclusion_with_options",
110        "execute_command" => "Bash",
111        "file_exists" => "FileExists",
112        "fileExists" => "FileExists",
113        "get_current_dir" => "GetCurrentDir",
114        "getCurrentDir" => "GetCurrentDir",
115        "get_file_info" => "GetFileInfo",
116        "getFileInfo" => "GetFileInfo",
117        "list_directory" => "Glob",
118        "memory_note" => "memory_note",
119        "read_file" => "Read",
120        "set_workspace" => "SetWorkspace",
121        "setWorkspace" => "SetWorkspace",
122        "sleep" => "Sleep",
123        "applyPatch" => "apply_patch",
124        "spawn_session" => "SubSession",
125        "spawnSession" => "SubSession",
126        "sub_session" => "SubSession",
127        "subSession" => "SubSession",
128        "sub_task" => "SubSession",
129        "subTask" => "SubSession",
130        "team_agent" => "SubSession",
131        "teamAgent" => "SubSession",
132        "child_session" => "SubSession",
133        "childSession" => "SubSession",
134        "sub_session_manager" => "SubSession",
135        "write_file" => "Write",
136        "sessionInspector" => "session_inspector",
137        "scheduleTasks" => "schedule_tasks",
138        _ => name,
139    }
140}
141
142/// Checks if a tool reference is a built-in tool
143pub fn is_builtin_tool(value: &str) -> bool {
144    normalize_tool_ref(value).is_some()
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn test_normalize_tool_ref_accepts_claude_style_names() {
153        assert_eq!(
154            normalize_tool_ref("default::Bash"),
155            Some("Bash".to_string())
156        );
157    }
158
159    #[test]
160    fn test_normalize_tool_ref_accepts_legacy_camel_aliases() {
161        assert_eq!(
162            normalize_tool_ref("default::fileExists"),
163            Some("FileExists".to_string())
164        );
165        assert_eq!(
166            normalize_tool_ref("default::getCurrentDir"),
167            Some("GetCurrentDir".to_string())
168        );
169        assert_eq!(
170            normalize_tool_ref("default::getFileInfo"),
171            Some("GetFileInfo".to_string())
172        );
173        assert_eq!(
174            normalize_tool_ref("default::setWorkspace"),
175            Some("SetWorkspace".to_string())
176        );
177        assert_eq!(
178            normalize_tool_ref("default::sleep"),
179            Some("Sleep".to_string())
180        );
181    }
182
183    #[test]
184    fn test_normalize_tool_ref_accepts_legacy_snake_case_aliases() {
185        assert_eq!(
186            normalize_tool_ref("default::execute_command"),
187            Some("Bash".to_string())
188        );
189        assert_eq!(
190            normalize_tool_ref("default::file_exists"),
191            Some("FileExists".to_string())
192        );
193        assert_eq!(
194            normalize_tool_ref("default::get_current_dir"),
195            Some("GetCurrentDir".to_string())
196        );
197        assert_eq!(
198            normalize_tool_ref("default::get_file_info"),
199            Some("GetFileInfo".to_string())
200        );
201        assert_eq!(
202            normalize_tool_ref("default::list_directory"),
203            Some("Glob".to_string())
204        );
205        assert_eq!(
206            normalize_tool_ref("default::memory_note"),
207            Some("memory_note".to_string())
208        );
209        assert_eq!(
210            normalize_tool_ref("default::read_file"),
211            Some("Read".to_string())
212        );
213        assert_eq!(
214            normalize_tool_ref("default::set_workspace"),
215            Some("SetWorkspace".to_string())
216        );
217        assert_eq!(
218            normalize_tool_ref("default::write_file"),
219            Some("Write".to_string())
220        );
221    }
222
223    #[test]
224    fn test_normalize_tool_ref_accepts_spawn_task_aliases() {
225        for alias in [
226            "default::spawn_session",
227            "default::sub_session",
228            "default::sub_task",
229            "default::team_agent",
230            "default::child_session",
231        ] {
232            assert_eq!(normalize_tool_ref(alias), Some("SubSession".to_string()));
233        }
234    }
235
236    #[test]
237    fn test_normalize_tool_ref_accepts_server_overlay_tools() {
238        assert_eq!(normalize_tool_ref("compress_context"), None);
239        assert_eq!(
240            normalize_tool_ref("default::read_skill_resource"),
241            Some("read_skill_resource".to_string())
242        );
243    }
244
245    #[test]
246    fn test_normalize_tool_ref_rejects_unknown_tool() {
247        assert_eq!(normalize_tool_ref("default::search"), None);
248    }
249
250    #[test]
251    fn test_is_builtin_tool() {
252        assert!(is_builtin_tool("Bash"));
253        assert!(is_builtin_tool("default::Bash"));
254        assert!(!is_builtin_tool("unknown_tool"));
255        assert!(!is_builtin_tool("compress_context"));
256    }
257
258    #[test]
259    fn test_resolve_alias() {
260        assert_eq!(resolve_alias("apply_patch"), Some("Edit"));
261        assert_eq!(resolve_alias("FileExists"), Some("GetFileInfo"));
262        assert_eq!(resolve_alias("Bash"), None);
263    }
264}