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    /// Web Fetch tool security configuration
43    #[serde(default)]
44    pub web_fetch: WebFetchConfig,
45
46    /// Dynamic plugin runtime configuration
47    #[serde(default)]
48    pub plugins: PluginRuntimeConfig,
49
50    /// Tool-specific loop thresholds (Adaptive Loop Detection)
51    /// Allows setting higher loop limits for read-only tools (e.g., ls, grep)
52    /// and lower limits for mutating tools.
53    #[serde(default)]
54    pub loop_thresholds: IndexMap<String, usize>,
55}
56
57/// Web Fetch tool security configuration
58#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
59#[derive(Debug, Clone, Deserialize, Serialize)]
60pub struct WebFetchConfig {
61    /// Security mode: "restricted" (blocklist) or "whitelist" (allowlist)
62    #[serde(default = "default_web_fetch_mode")]
63    pub mode: String,
64
65    /// Enable dynamic blocklist loading from external file
66    #[serde(default)]
67    pub dynamic_blocklist_enabled: bool,
68
69    /// Path to dynamic blocklist file
70    #[serde(default)]
71    pub dynamic_blocklist_path: String,
72
73    /// Enable dynamic whitelist loading from external file
74    #[serde(default)]
75    pub dynamic_whitelist_enabled: bool,
76
77    /// Path to dynamic whitelist file
78    #[serde(default)]
79    pub dynamic_whitelist_path: String,
80
81    /// Inline blocklist - Additional domains to block
82    #[serde(default)]
83    pub blocked_domains: Vec<String>,
84
85    /// Inline whitelist - Domains to allow in restricted mode
86    #[serde(default)]
87    pub allowed_domains: Vec<String>,
88
89    /// Additional blocked patterns
90    #[serde(default)]
91    pub blocked_patterns: Vec<String>,
92
93    /// Enable audit logging of URL validation decisions
94    #[serde(default)]
95    pub enable_audit_logging: bool,
96
97    /// Path to audit log file
98    #[serde(default)]
99    pub audit_log_path: String,
100
101    /// Strict HTTPS-only mode
102    #[serde(default = "default_strict_https")]
103    pub strict_https_only: bool,
104}
105
106impl Default for ToolsConfig {
107    fn default() -> Self {
108        let policies = DEFAULT_TOOL_POLICIES
109            .iter()
110            .map(|(tool, policy)| ((*tool).into(), *policy))
111            .collect::<IndexMap<_, _>>();
112        Self {
113            default_policy: default_tool_policy(),
114            policies,
115            max_tool_loops: default_max_tool_loops(),
116            max_repeated_tool_calls: default_max_repeated_tool_calls(),
117            max_tool_rate_per_second: default_max_tool_rate_per_second(),
118            web_fetch: WebFetchConfig::default(),
119            plugins: PluginRuntimeConfig::default(),
120            loop_thresholds: IndexMap::new(),
121        }
122    }
123}
124
125const DEFAULT_BLOCKLIST_PATH: &str = "~/.vtcode/web_fetch_blocklist.json";
126const DEFAULT_WHITELIST_PATH: &str = "~/.vtcode/web_fetch_whitelist.json";
127const DEFAULT_AUDIT_LOG_PATH: &str = "~/.vtcode/web_fetch_audit.log";
128
129impl Default for WebFetchConfig {
130    fn default() -> Self {
131        Self {
132            mode: default_web_fetch_mode(),
133            dynamic_blocklist_enabled: false,
134            dynamic_blocklist_path: DEFAULT_BLOCKLIST_PATH.into(),
135            dynamic_whitelist_enabled: false,
136            dynamic_whitelist_path: DEFAULT_WHITELIST_PATH.into(),
137            blocked_domains: Vec::new(),
138            allowed_domains: Vec::new(),
139            blocked_patterns: Vec::new(),
140            enable_audit_logging: false,
141            audit_log_path: DEFAULT_AUDIT_LOG_PATH.into(),
142            strict_https_only: true,
143        }
144    }
145}
146
147/// Tool execution policy
148#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
149#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
150#[serde(rename_all = "lowercase")]
151pub enum ToolPolicy {
152    /// Allow execution without confirmation
153    Allow,
154    /// Prompt user for confirmation
155    Prompt,
156    /// Deny execution
157    Deny,
158}
159
160#[inline]
161const fn default_tool_policy() -> ToolPolicy {
162    ToolPolicy::Prompt
163}
164
165#[inline]
166const fn default_max_tool_loops() -> usize {
167    defaults::DEFAULT_MAX_TOOL_LOOPS
168}
169
170#[inline]
171const fn default_max_repeated_tool_calls() -> usize {
172    defaults::DEFAULT_MAX_REPEATED_TOOL_CALLS
173}
174
175#[inline]
176const fn default_max_tool_rate_per_second() -> Option<usize> {
177    None
178}
179
180#[inline]
181fn default_web_fetch_mode() -> String {
182    "restricted".into()
183}
184
185fn default_strict_https() -> bool {
186    true
187}
188
189const DEFAULT_TOOL_POLICIES: &[(&str, ToolPolicy)] = &[
190    // File operations (non-destructive)
191    (tools::LIST_FILES, ToolPolicy::Allow),
192    (tools::GREP_FILE, ToolPolicy::Allow),
193    (tools::READ_FILE, ToolPolicy::Allow),
194    // File operations (write/create)
195    (tools::WRITE_FILE, ToolPolicy::Allow),
196    (tools::EDIT_FILE, ToolPolicy::Allow),
197    (tools::CREATE_FILE, ToolPolicy::Allow),
198    // File operations (destructive - require confirmation)
199    (tools::DELETE_FILE, ToolPolicy::Prompt),
200    (tools::APPLY_PATCH, ToolPolicy::Prompt),
201    (tools::SEARCH_REPLACE, ToolPolicy::Prompt),
202    // PTY/Terminal operations
203    (tools::RUN_PTY_CMD, ToolPolicy::Prompt),
204    (tools::CREATE_PTY_SESSION, ToolPolicy::Allow),
205    (tools::READ_PTY_SESSION, ToolPolicy::Allow),
206    (tools::LIST_PTY_SESSIONS, ToolPolicy::Allow),
207    (tools::RESIZE_PTY_SESSION, ToolPolicy::Allow),
208    (tools::SEND_PTY_INPUT, ToolPolicy::Prompt),
209    (tools::CLOSE_PTY_SESSION, ToolPolicy::Allow),
210    // Code execution (requires confirmation)
211    (tools::EXECUTE_CODE, ToolPolicy::Prompt),
212    // Planning and meta tools
213    (tools::SEARCH_TOOLS, ToolPolicy::Allow),
214    (tools::SKILL, ToolPolicy::Allow),
215    // Diagnostic and introspection tools
216    // Web operations (requires confirmation)
217    (tools::WEB_FETCH, ToolPolicy::Prompt),
218];