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