Skip to main content

vtcode_config/core/
tools.rs

1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3
4use crate::constants::{defaults, tools};
5use crate::core::plugins::PluginRuntimeConfig;
6
7/// Tools configuration
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct ToolsConfig {
11    /// Default policy for tools not explicitly listed
12    #[serde(default = "default_tool_policy")]
13    pub default_policy: ToolPolicy,
14
15    /// Specific tool policies
16    #[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    /// Maximum inner tool-call loops per user turn
24    ///
25    /// Prevents infinite tool-calling cycles in interactive chat. This limits how
26    /// many back-and-forths the agent will perform executing tools and
27    /// re-asking the model before returning a final answer.
28    ///
29    #[serde(default = "default_max_tool_loops")]
30    pub max_tool_loops: usize,
31
32    /// Maximum number of times the same tool invocation can be retried with the
33    /// identical arguments within a single turn.
34    #[serde(default = "default_max_repeated_tool_calls")]
35    pub max_repeated_tool_calls: usize,
36
37    /// Maximum consecutive blocked tool calls allowed per turn before forcing a
38    /// turn break. This prevents long blocked-call churn from consuming CPU.
39    #[serde(default = "default_max_consecutive_blocked_tool_calls_per_turn")]
40    pub max_consecutive_blocked_tool_calls_per_turn: usize,
41
42    /// Optional per-second rate limit for tool calls to smooth bursty retries.
43    /// When unset, the runtime defaults apply.
44    #[serde(default = "default_max_tool_rate_per_second")]
45    pub max_tool_rate_per_second: Option<usize>,
46
47    /// Maximum sequential spool-chunk `read_file` calls allowed per turn before
48    /// nudging the agent to switch to targeted extraction/summarization.
49    #[serde(default = "default_max_sequential_spool_chunk_reads")]
50    pub max_sequential_spool_chunk_reads: usize,
51
52    /// Web Fetch tool security configuration
53    #[serde(default)]
54    pub web_fetch: WebFetchConfig,
55
56    /// Dynamic plugin runtime configuration
57    #[serde(default)]
58    pub plugins: PluginRuntimeConfig,
59
60    /// Tool-specific loop thresholds (Adaptive Loop Detection)
61    /// Allows setting higher loop limits for read-only tools (e.g., ls, grep)
62    /// and lower limits for mutating tools.
63    #[serde(default)]
64    pub loop_thresholds: IndexMap<String, usize>,
65}
66
67/// Web Fetch tool security configuration
68#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
69#[derive(Debug, Clone, Deserialize, Serialize)]
70pub struct WebFetchConfig {
71    /// Security mode: "restricted" (blocklist) or "whitelist" (allowlist)
72    #[serde(default = "default_web_fetch_mode")]
73    pub mode: String,
74
75    /// Enable dynamic blocklist loading from external file
76    #[serde(default)]
77    pub dynamic_blocklist_enabled: bool,
78
79    /// Path to dynamic blocklist file
80    #[serde(default)]
81    pub dynamic_blocklist_path: String,
82
83    /// Enable dynamic whitelist loading from external file
84    #[serde(default)]
85    pub dynamic_whitelist_enabled: bool,
86
87    /// Path to dynamic whitelist file
88    #[serde(default)]
89    pub dynamic_whitelist_path: String,
90
91    /// Inline blocklist - Additional domains to block
92    #[serde(default)]
93    pub blocked_domains: Vec<String>,
94
95    /// Inline whitelist - Domains to allow in restricted mode
96    #[serde(default)]
97    pub allowed_domains: Vec<String>,
98
99    /// Additional blocked patterns
100    #[serde(default)]
101    pub blocked_patterns: Vec<String>,
102
103    /// Enable audit logging of URL validation decisions
104    #[serde(default)]
105    pub enable_audit_logging: bool,
106
107    /// Path to audit log file
108    #[serde(default)]
109    pub audit_log_path: String,
110
111    /// Strict HTTPS-only mode
112    #[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/// Tool execution policy
161#[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 execution without confirmation
166    Allow,
167    /// Prompt user for confirmation
168    Prompt,
169    /// Deny execution
170    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    // File operations (non-destructive)
214    (tools::LIST_FILES, ToolPolicy::Allow),
215    (tools::GREP_FILE, ToolPolicy::Allow),
216    (tools::READ_FILE, ToolPolicy::Allow),
217    // File operations (write/create)
218    (tools::WRITE_FILE, ToolPolicy::Allow),
219    (tools::EDIT_FILE, ToolPolicy::Allow),
220    (tools::CREATE_FILE, ToolPolicy::Allow),
221    // File operations (destructive - require confirmation)
222    (tools::DELETE_FILE, ToolPolicy::Prompt),
223    (tools::APPLY_PATCH, ToolPolicy::Prompt),
224    (tools::SEARCH_REPLACE, ToolPolicy::Prompt),
225    // PTY/Terminal operations
226    (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    // Code execution (requires confirmation)
234    (tools::EXECUTE_CODE, ToolPolicy::Prompt),
235    // Planning and meta tools
236    (tools::SEARCH_TOOLS, ToolPolicy::Allow),
237    (tools::SKILL, ToolPolicy::Allow),
238    // Diagnostic and introspection tools
239    // Web operations (requires confirmation)
240    (tools::WEB_FETCH, ToolPolicy::Prompt),
241];