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