ai_agent/utils/settings/
tool_validation_config.rs1const FILE_PATTERN_TOOLS: &[&str] = &[
6 "Read", "Write", "Edit", "Glob", "NotebookRead", "NotebookEdit",
7];
8
9const BASH_PREFIX_TOOLS: &[&str] = &["Bash"];
11
12pub fn is_file_pattern_tool(tool_name: &str) -> bool {
14 FILE_PATTERN_TOOLS
15 .iter()
16 .any(|t| t.eq_ignore_ascii_case(tool_name))
17}
18
19pub fn is_bash_prefix_tool(tool_name: &str) -> bool {
21 BASH_PREFIX_TOOLS
22 .iter()
23 .any(|t| t.eq_ignore_ascii_case(tool_name))
24}
25
26pub type CustomValidationResult = CustomValidateResult;
28
29#[derive(Debug, Clone)]
31pub struct CustomValidateResult {
32 pub valid: bool,
33 pub error: Option<String>,
34 pub suggestion: Option<String>,
35 pub examples: Option<Vec<String>>,
36}
37
38pub fn get_custom_validation(content: &str) -> Option<CustomValidateResult> {
40 if content.starts_with("WebSearch") {
42 let check = if content.contains('*') || content.contains('?') {
43 CustomValidateResult {
44 valid: false,
45 error: Some("WebSearch does not support wildcards".into()),
46 suggestion: Some("Use exact search terms without * or ?".into()),
47 examples: Some(vec![
48 "WebSearch(claude ai)".into(),
49 "WebSearch(typescript tutorial)".into(),
50 ]),
51 }
52 } else {
53 CustomValidateResult {
54 valid: true,
55 error: None,
56 suggestion: None,
57 examples: None,
58 }
59 };
60 return Some(check);
61 }
62
63 if content.starts_with("WebFetch") {
65 if content.contains("://") || content.starts_with("http") {
66 return Some(CustomValidateResult {
67 valid: false,
68 error: Some("WebFetch permissions use domain format, not URLs".into()),
69 suggestion: Some("Use \"domain:hostname\" format".into()),
70 examples: Some(vec![
71 "WebFetch(domain:example.com)".into(),
72 "WebFetch(domain:github.com)".into(),
73 ]),
74 });
75 }
76 if !content.contains("domain:") {
77 return Some(CustomValidateResult {
78 valid: false,
79 error: Some("WebFetch permissions must use \"domain:\" prefix".into()),
80 suggestion: Some("Use \"domain:hostname\" format".into()),
81 examples: Some(vec![
82 "WebFetch(domain:example.com)".into(),
83 "WebFetch(domain:*.google.com)".into(),
84 ]),
85 });
86 }
87 return Some(CustomValidateResult {
88 valid: true,
89 error: None,
90 suggestion: None,
91 examples: None,
92 });
93 }
94
95 None
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_is_file_pattern_tool() {
104 assert!(is_file_pattern_tool("Read"));
105 assert!(is_file_pattern_tool("Write"));
106 assert!(is_file_pattern_tool("Edit"));
107 assert!(is_file_pattern_tool("Glob"));
108 assert!(!is_file_pattern_tool("Bash"));
109 assert!(!is_file_pattern_tool("WebSearch"));
110 }
111
112 #[test]
113 fn test_is_bash_prefix_tool() {
114 assert!(is_bash_prefix_tool("Bash"));
115 assert!(!is_bash_prefix_tool("Read"));
116 assert!(!is_bash_prefix_tool("WebSearch"));
117 }
118
119 #[test]
120 fn test_custom_validation_websearch() {
121 let result = get_custom_validation("WebSearch(claude ai)");
122 assert!(result.is_none() || result.unwrap().valid);
123
124 let result = get_custom_validation("WebSearch(claude*)");
126 assert!(result.is_some());
127 let result = result.unwrap();
128 assert!(!result.valid);
129 }
130
131 #[test]
132 fn test_custom_validation_webfetch() {
133 let result = get_custom_validation("WebFetch(domain:example.com)");
135 assert!(result.is_some());
136 assert!(result.unwrap().valid);
137
138 let result = get_custom_validation("WebFetch(https://example.com)");
140 assert!(result.is_some());
141 assert!(!result.unwrap().valid);
142 }
143
144 #[test]
145 fn test_no_custom_validation_for_other_tools() {
146 assert!(get_custom_validation("Bash(npm install)").is_none());
147 assert!(get_custom_validation("Read(*.ts)").is_none());
148 }
149}