1pub const BUILTIN_TOOL_NAMES: [&str; 23] = [
12 "conclusion_with_options",
13 "Bash",
14 "BashInput",
15 "BashOutput",
16 "Edit",
17 "EnterPlanMode",
18 "ExitPlanMode",
19 "GetFileInfo",
20 "Glob",
21 "Grep",
22 "js_repl",
23 "KillShell",
24 "session_note",
25 "NotebookEdit",
26 "Read",
27 "request_permissions",
28 "Sleep",
29 "Task",
30 "update_goal",
31 "WebFetch",
32 "WebSearch",
33 "Workspace",
34 "Write",
35];
36
37pub const BUILTIN_TOOL_ALIASES: [(&str, &str); 10] = [
41 ("apply_patch", "Edit"),
43 ("FileExists", "GetFileInfo"),
45 ("GetCurrentDir", "Workspace"),
47 ("SetWorkspace", "Workspace"),
48 ("memory_note", "session_note"),
50 ("recall", "session_history"),
55 ("session_inspector", "session_history"),
56 ("schedule_tasks", "scheduler"),
57 ("sub_session_manager", "SubAgent"),
59 ("SubSession", "SubAgent"),
60];
61
62pub const SERVER_TOOL_NAMES: [&str; 7] = [
63 "SubAgent",
64 "compact_context",
65 "scheduler",
66 "session_history",
67 "memory",
68 "load_skill",
69 "read_skill_resource",
70];
71
72pub fn normalize_tool_ref(value: &str) -> Option<String> {
76 let trimmed = value.trim();
77 if trimmed.is_empty() {
78 return None;
79 }
80 let raw_tool_name = trimmed.split("::").last().unwrap_or(trimmed);
81 let normalized = normalize_builtin_alias(raw_tool_name);
82
83 if let Some(name) = BUILTIN_TOOL_NAMES
85 .iter()
86 .chain(SERVER_TOOL_NAMES.iter())
87 .find(|name| name.eq_ignore_ascii_case(normalized))
88 {
89 return Some((*name).to_string());
90 }
91
92 BUILTIN_TOOL_ALIASES
95 .iter()
96 .find(|(alias, _)| alias.eq_ignore_ascii_case(normalized))
97 .map(|(alias, _)| (*alias).to_string())
98}
99
100pub fn resolve_alias(name: &str) -> Option<&'static str> {
103 BUILTIN_TOOL_ALIASES
104 .iter()
105 .find(|(alias, _)| alias.eq_ignore_ascii_case(name))
106 .map(|(_, canonical)| *canonical)
107}
108
109pub fn normalize_builtin_alias(name: &str) -> &str {
110 match name {
111 "conclusionWithOptions" => "conclusion_with_options",
113 "execute_command" => "Bash",
114 "file_exists" => "FileExists",
115 "fileExists" => "FileExists",
116 "get_current_dir" => "GetCurrentDir",
117 "getCurrentDir" => "GetCurrentDir",
118 "get_file_info" => "GetFileInfo",
119 "getFileInfo" => "GetFileInfo",
120 "list_directory" => "Glob",
121 "memory_note" => "memory_note",
122 "read_file" => "Read",
123 "set_workspace" => "SetWorkspace",
124 "setWorkspace" => "SetWorkspace",
125 "sleep" => "Sleep",
126 "applyPatch" => "apply_patch",
127 "spawn_session" => "SubAgent",
128 "spawnSession" => "SubAgent",
129 "sub_session" => "SubAgent",
130 "subSession" => "SubAgent",
131 "sub_task" => "SubAgent",
132 "subTask" => "SubAgent",
133 "team_agent" => "SubAgent",
134 "teamAgent" => "SubAgent",
135 "child_session" => "SubAgent",
136 "childSession" => "SubAgent",
137 "sub_session_manager" => "SubAgent",
138 "sub_agent" => "SubAgent",
139 "subAgent" => "SubAgent",
140 "SubSession" => "SubAgent",
141 "subsession" => "SubAgent",
142 "write_file" => "Write",
143 "sessionInspector" => "session_inspector",
144 "scheduleTasks" => "schedule_tasks",
145 _ => name,
146 }
147}
148
149pub fn is_builtin_tool(value: &str) -> bool {
151 normalize_tool_ref(value).is_some()
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_normalize_tool_ref_accepts_claude_style_names() {
160 assert_eq!(
161 normalize_tool_ref("default::Bash"),
162 Some("Bash".to_string())
163 );
164 }
165
166 #[test]
167 fn test_normalize_tool_ref_accepts_legacy_camel_aliases() {
168 assert_eq!(
169 normalize_tool_ref("default::fileExists"),
170 Some("FileExists".to_string())
171 );
172 assert_eq!(
173 normalize_tool_ref("default::getCurrentDir"),
174 Some("GetCurrentDir".to_string())
175 );
176 assert_eq!(
177 normalize_tool_ref("default::getFileInfo"),
178 Some("GetFileInfo".to_string())
179 );
180 assert_eq!(
181 normalize_tool_ref("default::setWorkspace"),
182 Some("SetWorkspace".to_string())
183 );
184 assert_eq!(
185 normalize_tool_ref("default::sleep"),
186 Some("Sleep".to_string())
187 );
188 }
189
190 #[test]
191 fn test_normalize_tool_ref_accepts_legacy_snake_case_aliases() {
192 assert_eq!(
193 normalize_tool_ref("default::execute_command"),
194 Some("Bash".to_string())
195 );
196 assert_eq!(
197 normalize_tool_ref("default::file_exists"),
198 Some("FileExists".to_string())
199 );
200 assert_eq!(
201 normalize_tool_ref("default::get_current_dir"),
202 Some("GetCurrentDir".to_string())
203 );
204 assert_eq!(
205 normalize_tool_ref("default::get_file_info"),
206 Some("GetFileInfo".to_string())
207 );
208 assert_eq!(
209 normalize_tool_ref("default::list_directory"),
210 Some("Glob".to_string())
211 );
212 assert_eq!(
213 normalize_tool_ref("default::memory_note"),
214 Some("memory_note".to_string())
215 );
216 assert_eq!(
217 normalize_tool_ref("default::read_file"),
218 Some("Read".to_string())
219 );
220 assert_eq!(
221 normalize_tool_ref("default::set_workspace"),
222 Some("SetWorkspace".to_string())
223 );
224 assert_eq!(
225 normalize_tool_ref("default::write_file"),
226 Some("Write".to_string())
227 );
228 }
229
230 #[test]
231 fn test_normalize_tool_ref_accepts_spawn_task_aliases() {
232 for alias in [
233 "default::spawn_session",
234 "default::sub_session",
235 "default::sub_task",
236 "default::team_agent",
237 "default::child_session",
238 "default::sub_agent",
239 "default::subAgent",
240 "default::SubSession",
241 "default::subsession",
242 ] {
243 assert_eq!(normalize_tool_ref(alias), Some("SubAgent".to_string()));
244 }
245 }
246
247 #[test]
248 fn test_normalize_tool_ref_accepts_server_overlay_tools() {
249 assert_eq!(normalize_tool_ref("compress_context"), None);
250 assert_eq!(
251 normalize_tool_ref("default::read_skill_resource"),
252 Some("read_skill_resource".to_string())
253 );
254 }
255
256 #[test]
257 fn test_normalize_tool_ref_rejects_unknown_tool() {
258 assert_eq!(normalize_tool_ref("default::search"), None);
259 }
260
261 #[test]
262 fn test_is_builtin_tool() {
263 assert!(is_builtin_tool("Bash"));
264 assert!(is_builtin_tool("default::Bash"));
265 assert!(!is_builtin_tool("unknown_tool"));
266 assert!(!is_builtin_tool("compress_context"));
267 }
268
269 #[test]
270 fn test_resolve_alias() {
271 assert_eq!(resolve_alias("apply_patch"), Some("Edit"));
272 assert_eq!(resolve_alias("FileExists"), Some("GetFileInfo"));
273 assert_eq!(resolve_alias("Bash"), None);
274 }
275}