1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3
4use crate::constants::{defaults, tools};
5use crate::core::plugins::PluginRuntimeConfig;
6
7#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct ToolsConfig {
11 #[serde(default = "default_tool_policy")]
13 pub default_policy: ToolPolicy,
14
15 #[serde(default)]
17 #[cfg_attr(
18 feature = "schema",
19 schemars(with = "std::collections::BTreeMap<String, ToolPolicy>")
20 )]
21 pub policies: IndexMap<String, ToolPolicy>,
22
23 #[serde(default = "default_max_tool_loops")]
30 pub max_tool_loops: usize,
31
32 #[serde(default = "default_max_repeated_tool_calls")]
35 pub max_repeated_tool_calls: usize,
36
37 #[serde(default = "default_max_tool_rate_per_second")]
40 pub max_tool_rate_per_second: Option<usize>,
41
42 #[serde(default = "default_max_sequential_spool_chunk_reads")]
45 pub max_sequential_spool_chunk_reads: usize,
46
47 #[serde(default)]
49 pub web_fetch: WebFetchConfig,
50
51 #[serde(default)]
53 pub plugins: PluginRuntimeConfig,
54
55 #[serde(default)]
59 pub loop_thresholds: IndexMap<String, usize>,
60}
61
62#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
64#[derive(Debug, Clone, Deserialize, Serialize)]
65pub struct WebFetchConfig {
66 #[serde(default = "default_web_fetch_mode")]
68 pub mode: String,
69
70 #[serde(default)]
72 pub dynamic_blocklist_enabled: bool,
73
74 #[serde(default)]
76 pub dynamic_blocklist_path: String,
77
78 #[serde(default)]
80 pub dynamic_whitelist_enabled: bool,
81
82 #[serde(default)]
84 pub dynamic_whitelist_path: String,
85
86 #[serde(default)]
88 pub blocked_domains: Vec<String>,
89
90 #[serde(default)]
92 pub allowed_domains: Vec<String>,
93
94 #[serde(default)]
96 pub blocked_patterns: Vec<String>,
97
98 #[serde(default)]
100 pub enable_audit_logging: bool,
101
102 #[serde(default)]
104 pub audit_log_path: String,
105
106 #[serde(default = "default_strict_https")]
108 pub strict_https_only: bool,
109}
110
111impl Default for ToolsConfig {
112 fn default() -> Self {
113 let policies = DEFAULT_TOOL_POLICIES
114 .iter()
115 .map(|(tool, policy)| ((*tool).into(), *policy))
116 .collect::<IndexMap<_, _>>();
117 Self {
118 default_policy: default_tool_policy(),
119 policies,
120 max_tool_loops: default_max_tool_loops(),
121 max_repeated_tool_calls: default_max_repeated_tool_calls(),
122 max_tool_rate_per_second: default_max_tool_rate_per_second(),
123 max_sequential_spool_chunk_reads: default_max_sequential_spool_chunk_reads(),
124 web_fetch: WebFetchConfig::default(),
125 plugins: PluginRuntimeConfig::default(),
126 loop_thresholds: IndexMap::new(),
127 }
128 }
129}
130
131const DEFAULT_BLOCKLIST_PATH: &str = "~/.vtcode/web_fetch_blocklist.json";
132const DEFAULT_WHITELIST_PATH: &str = "~/.vtcode/web_fetch_whitelist.json";
133const DEFAULT_AUDIT_LOG_PATH: &str = "~/.vtcode/web_fetch_audit.log";
134
135impl Default for WebFetchConfig {
136 fn default() -> Self {
137 Self {
138 mode: default_web_fetch_mode(),
139 dynamic_blocklist_enabled: false,
140 dynamic_blocklist_path: DEFAULT_BLOCKLIST_PATH.into(),
141 dynamic_whitelist_enabled: false,
142 dynamic_whitelist_path: DEFAULT_WHITELIST_PATH.into(),
143 blocked_domains: Vec::new(),
144 allowed_domains: Vec::new(),
145 blocked_patterns: Vec::new(),
146 enable_audit_logging: false,
147 audit_log_path: DEFAULT_AUDIT_LOG_PATH.into(),
148 strict_https_only: true,
149 }
150 }
151}
152
153#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
155#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
156#[serde(rename_all = "lowercase")]
157pub enum ToolPolicy {
158 Allow,
160 Prompt,
162 Deny,
164}
165
166#[inline]
167const fn default_tool_policy() -> ToolPolicy {
168 ToolPolicy::Prompt
169}
170
171#[inline]
172const fn default_max_tool_loops() -> usize {
173 defaults::DEFAULT_MAX_TOOL_LOOPS
174}
175
176#[inline]
177const fn default_max_repeated_tool_calls() -> usize {
178 defaults::DEFAULT_MAX_REPEATED_TOOL_CALLS
179}
180
181#[inline]
182const fn default_max_tool_rate_per_second() -> Option<usize> {
183 None
184}
185
186#[inline]
187const fn default_max_sequential_spool_chunk_reads() -> usize {
188 defaults::DEFAULT_MAX_SEQUENTIAL_SPOOL_CHUNK_READS_PER_TURN
189}
190
191#[inline]
192fn default_web_fetch_mode() -> String {
193 "restricted".into()
194}
195
196fn default_strict_https() -> bool {
197 true
198}
199
200const DEFAULT_TOOL_POLICIES: &[(&str, ToolPolicy)] = &[
201 (tools::LIST_FILES, ToolPolicy::Allow),
203 (tools::GREP_FILE, ToolPolicy::Allow),
204 (tools::READ_FILE, ToolPolicy::Allow),
205 (tools::WRITE_FILE, ToolPolicy::Allow),
207 (tools::EDIT_FILE, ToolPolicy::Allow),
208 (tools::CREATE_FILE, ToolPolicy::Allow),
209 (tools::DELETE_FILE, ToolPolicy::Prompt),
211 (tools::APPLY_PATCH, ToolPolicy::Prompt),
212 (tools::SEARCH_REPLACE, ToolPolicy::Prompt),
213 (tools::RUN_PTY_CMD, ToolPolicy::Prompt),
215 (tools::CREATE_PTY_SESSION, ToolPolicy::Allow),
216 (tools::READ_PTY_SESSION, ToolPolicy::Allow),
217 (tools::LIST_PTY_SESSIONS, ToolPolicy::Allow),
218 (tools::RESIZE_PTY_SESSION, ToolPolicy::Allow),
219 (tools::SEND_PTY_INPUT, ToolPolicy::Prompt),
220 (tools::CLOSE_PTY_SESSION, ToolPolicy::Allow),
221 (tools::EXECUTE_CODE, ToolPolicy::Prompt),
223 (tools::SEARCH_TOOLS, ToolPolicy::Allow),
225 (tools::SKILL, ToolPolicy::Allow),
226 (tools::WEB_FETCH, ToolPolicy::Prompt),
229];