1pub 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
35pub const BUILTIN_TOOL_ALIASES: [(&str, &str); 9] = [
39 ("apply_patch", "Edit"),
41 ("FileExists", "GetFileInfo"),
43 ("GetCurrentDir", "Workspace"),
45 ("SetWorkspace", "Workspace"),
46 ("memory_note", "session_note"),
48 ("recall", "session_history"),
53 ("session_inspector", "session_history"),
54 ("schedule_tasks", "scheduler"),
55 ("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
69pub 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 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 BUILTIN_TOOL_ALIASES
92 .iter()
93 .find(|(alias, _)| alias.eq_ignore_ascii_case(normalized))
94 .map(|(alias, _)| (*alias).to_string())
95}
96
97pub 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 "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
142pub 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}