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_consecutive_blocked_tool_calls_per_turn")]
40 pub max_consecutive_blocked_tool_calls_per_turn: usize,
41
42 #[serde(default = "default_max_tool_rate_per_second")]
45 pub max_tool_rate_per_second: Option<usize>,
46
47 #[serde(default = "default_max_sequential_spool_chunk_reads")]
50 pub max_sequential_spool_chunk_reads: usize,
51
52 #[serde(default)]
54 pub web_fetch: WebFetchConfig,
55
56 #[serde(default)]
58 pub plugins: PluginRuntimeConfig,
59
60 #[serde(default)]
64 pub loop_thresholds: IndexMap<String, usize>,
65}
66
67#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
69#[derive(Debug, Clone, Deserialize, Serialize)]
70pub struct WebFetchConfig {
71 #[serde(default = "default_web_fetch_mode")]
73 pub mode: String,
74
75 #[serde(default)]
77 pub dynamic_blocklist_enabled: bool,
78
79 #[serde(default)]
81 pub dynamic_blocklist_path: String,
82
83 #[serde(default)]
85 pub dynamic_whitelist_enabled: bool,
86
87 #[serde(default)]
89 pub dynamic_whitelist_path: String,
90
91 #[serde(default)]
93 pub blocked_domains: Vec<String>,
94
95 #[serde(default)]
97 pub allowed_domains: Vec<String>,
98
99 #[serde(default)]
101 pub blocked_patterns: Vec<String>,
102
103 #[serde(default)]
105 pub enable_audit_logging: bool,
106
107 #[serde(default)]
109 pub audit_log_path: String,
110
111 #[serde(default = "default_strict_https")]
113 pub strict_https_only: bool,
114}
115
116impl Default for ToolsConfig {
117 fn default() -> Self {
118 let policies = DEFAULT_TOOL_POLICIES
119 .iter()
120 .map(|(tool, policy)| ((*tool).into(), *policy))
121 .collect::<IndexMap<_, _>>();
122 Self {
123 default_policy: default_tool_policy(),
124 policies,
125 max_tool_loops: default_max_tool_loops(),
126 max_repeated_tool_calls: default_max_repeated_tool_calls(),
127 max_consecutive_blocked_tool_calls_per_turn:
128 default_max_consecutive_blocked_tool_calls_per_turn(),
129 max_tool_rate_per_second: default_max_tool_rate_per_second(),
130 max_sequential_spool_chunk_reads: default_max_sequential_spool_chunk_reads(),
131 web_fetch: WebFetchConfig::default(),
132 plugins: PluginRuntimeConfig::default(),
133 loop_thresholds: IndexMap::new(),
134 }
135 }
136}
137
138const DEFAULT_BLOCKLIST_PATH: &str = "~/.vtcode/web_fetch_blocklist.json";
139const DEFAULT_WHITELIST_PATH: &str = "~/.vtcode/web_fetch_whitelist.json";
140const DEFAULT_AUDIT_LOG_PATH: &str = "~/.vtcode/web_fetch_audit.log";
141
142impl Default for WebFetchConfig {
143 fn default() -> Self {
144 Self {
145 mode: default_web_fetch_mode(),
146 dynamic_blocklist_enabled: false,
147 dynamic_blocklist_path: DEFAULT_BLOCKLIST_PATH.into(),
148 dynamic_whitelist_enabled: false,
149 dynamic_whitelist_path: DEFAULT_WHITELIST_PATH.into(),
150 blocked_domains: Vec::new(),
151 allowed_domains: Vec::new(),
152 blocked_patterns: Vec::new(),
153 enable_audit_logging: false,
154 audit_log_path: DEFAULT_AUDIT_LOG_PATH.into(),
155 strict_https_only: true,
156 }
157 }
158}
159
160#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
162#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
163#[serde(rename_all = "lowercase")]
164pub enum ToolPolicy {
165 Allow,
167 Prompt,
169 Deny,
171}
172
173#[inline]
174const fn default_tool_policy() -> ToolPolicy {
175 ToolPolicy::Prompt
176}
177
178#[inline]
179const fn default_max_tool_loops() -> usize {
180 defaults::DEFAULT_MAX_TOOL_LOOPS
181}
182
183#[inline]
184const fn default_max_repeated_tool_calls() -> usize {
185 defaults::DEFAULT_MAX_REPEATED_TOOL_CALLS
186}
187
188#[inline]
189const fn default_max_consecutive_blocked_tool_calls_per_turn() -> usize {
190 defaults::DEFAULT_MAX_CONSECUTIVE_BLOCKED_TOOL_CALLS_PER_TURN
191}
192
193#[inline]
194const fn default_max_tool_rate_per_second() -> Option<usize> {
195 None
196}
197
198#[inline]
199const fn default_max_sequential_spool_chunk_reads() -> usize {
200 defaults::DEFAULT_MAX_SEQUENTIAL_SPOOL_CHUNK_READS_PER_TURN
201}
202
203#[inline]
204fn default_web_fetch_mode() -> String {
205 "restricted".into()
206}
207
208fn default_strict_https() -> bool {
209 true
210}
211
212const DEFAULT_TOOL_POLICIES: &[(&str, ToolPolicy)] = &[
213 (tools::LIST_FILES, ToolPolicy::Allow),
215 (tools::GREP_FILE, ToolPolicy::Allow),
216 (tools::READ_FILE, ToolPolicy::Allow),
217 (tools::WRITE_FILE, ToolPolicy::Allow),
219 (tools::EDIT_FILE, ToolPolicy::Allow),
220 (tools::CREATE_FILE, ToolPolicy::Allow),
221 (tools::DELETE_FILE, ToolPolicy::Prompt),
223 (tools::APPLY_PATCH, ToolPolicy::Prompt),
224 (tools::SEARCH_REPLACE, ToolPolicy::Prompt),
225 (tools::RUN_PTY_CMD, ToolPolicy::Prompt),
227 (tools::CREATE_PTY_SESSION, ToolPolicy::Allow),
228 (tools::READ_PTY_SESSION, ToolPolicy::Allow),
229 (tools::LIST_PTY_SESSIONS, ToolPolicy::Allow),
230 (tools::RESIZE_PTY_SESSION, ToolPolicy::Allow),
231 (tools::SEND_PTY_INPUT, ToolPolicy::Prompt),
232 (tools::CLOSE_PTY_SESSION, ToolPolicy::Allow),
233 (tools::EXECUTE_CODE, ToolPolicy::Prompt),
235 (tools::SEARCH_TOOLS, ToolPolicy::Allow),
237 (tools::SKILL, ToolPolicy::Allow),
238 (tools::WEB_FETCH, ToolPolicy::Prompt),
241];