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    /// Optional per-second rate limit for tool calls to smooth bursty retries.
38    /// When unset, the runtime defaults apply.
39    #[serde(default = "default_max_tool_rate_per_second")]
40    pub max_tool_rate_per_second: Option<usize>,
41
42    /// Maximum sequential spool-chunk `read_file` calls allowed per turn before
43    /// nudging the agent to switch to targeted extraction/summarization.
44    #[serde(default = "default_max_sequential_spool_chunk_reads")]
45    pub max_sequential_spool_chunk_reads: usize,
46
47    /// Web Fetch tool security configuration
48    #[serde(default)]
49    pub web_fetch: WebFetchConfig,
50
51    /// Dynamic plugin runtime configuration
52    #[serde(default)]
53    pub plugins: PluginRuntimeConfig,
54
55    /// Tool-specific loop thresholds (Adaptive Loop Detection)
56    /// Allows setting higher loop limits for read-only tools (e.g., ls, grep)
57    /// and lower limits for mutating tools.
58    #[serde(default)]
59    pub loop_thresholds: IndexMap<String, usize>,
60}
61
62/// Web Fetch tool security configuration
63#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
64#[derive(Debug, Clone, Deserialize, Serialize)]
65pub struct WebFetchConfig {
66    /// Security mode: "restricted" (blocklist) or "whitelist" (allowlist)
67    #[serde(default = "default_web_fetch_mode")]
68    pub mode: String,
69
70    /// Enable dynamic blocklist loading from external file
71    #[serde(default)]
72    pub dynamic_blocklist_enabled: bool,
73
74    /// Path to dynamic blocklist file
75    #[serde(default)]
76    pub dynamic_blocklist_path: String,
77
78    /// Enable dynamic whitelist loading from external file
79    #[serde(default)]
80    pub dynamic_whitelist_enabled: bool,
81
82    /// Path to dynamic whitelist file
83    #[serde(default)]
84    pub dynamic_whitelist_path: String,
85
86    /// Inline blocklist - Additional domains to block
87    #[serde(default)]
88    pub blocked_domains: Vec<String>,
89
90    /// Inline whitelist - Domains to allow in restricted mode
91    #[serde(default)]
92    pub allowed_domains: Vec<String>,
93
94    /// Additional blocked patterns
95    #[serde(default)]
96    pub blocked_patterns: Vec<String>,
97
98    /// Enable audit logging of URL validation decisions
99    #[serde(default)]
100    pub enable_audit_logging: bool,
101
102    /// Path to audit log file
103    #[serde(default)]
104    pub audit_log_path: String,
105
106    /// Strict HTTPS-only mode
107    #[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/// Tool execution policy
154#[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 execution without confirmation
159    Allow,
160    /// Prompt user for confirmation
161    Prompt,
162    /// Deny execution
163    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    // File operations (non-destructive)
202    (tools::LIST_FILES, ToolPolicy::Allow),
203    (tools::GREP_FILE, ToolPolicy::Allow),
204    (tools::READ_FILE, ToolPolicy::Allow),
205    // File operations (write/create)
206    (tools::WRITE_FILE, ToolPolicy::Allow),
207    (tools::EDIT_FILE, ToolPolicy::Allow),
208    (tools::CREATE_FILE, ToolPolicy::Allow),
209    // File operations (destructive - require confirmation)
210    (tools::DELETE_FILE, ToolPolicy::Prompt),
211    (tools::APPLY_PATCH, ToolPolicy::Prompt),
212    (tools::SEARCH_REPLACE, ToolPolicy::Prompt),
213    // PTY/Terminal operations
214    (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    // Code execution (requires confirmation)
222    (tools::EXECUTE_CODE, ToolPolicy::Prompt),
223    // Planning and meta tools
224    (tools::SEARCH_TOOLS, ToolPolicy::Allow),
225    (tools::SKILL, ToolPolicy::Allow),
226    // Diagnostic and introspection tools
227    // Web operations (requires confirmation)
228    (tools::WEB_FETCH, ToolPolicy::Prompt),
229];