Skip to main content

vtcode_core/utils/
safety.rs

1//! Safety checks for VT Code operations
2//!
3//! This module provides safety validations for potentially expensive
4//! or resource-intensive operations to ensure user control and efficiency.
5
6use crate::config::models::ModelId;
7use crate::ui::user_confirmation::{AgentMode, ProModelConfirmationResult, UserConfirmation};
8use crate::utils::colors::style;
9use anyhow::Result;
10use std::sync::atomic::{AtomicBool, Ordering};
11
12static PRO_MODEL_AUTO_ACCEPT: AtomicBool = AtomicBool::new(false);
13
14/// Safety validation utilities for VT Code operations
15pub struct SafetyValidator;
16
17impl SafetyValidator {
18    /// Validate and potentially request confirmation for model usage
19    /// Returns the approved model to use, which may be different from the requested model
20    pub fn validate_model_usage(
21        requested_model: &str,
22        task_description: Option<&str>,
23        skip_confirmations: bool,
24    ) -> Result<String> {
25        use crate::config::constants::models;
26        // Parse the requested model
27        let model_id = match requested_model {
28            s if s == models::google::GEMINI_3_1_PRO_PREVIEW => Some(ModelId::Gemini31ProPreview),
29            s if s == models::GEMINI_3_5_FLASH => Some(ModelId::Gemini35Flash),
30            _ => None,
31        };
32
33        // Check if this is the most capable (and expensive) model
34        if let Some(ModelId::Gemini31ProPreview) = model_id {
35            let current_default = ModelId::default();
36
37            if skip_confirmations {
38                println!(
39                    "{}",
40                    style("Using Gemini 3.1 Pro model (confirmations skipped)").cyan()
41                );
42                return Ok(requested_model.to_string());
43            }
44
45            if PRO_MODEL_AUTO_ACCEPT.load(Ordering::Relaxed) {
46                println!(
47                    "{}",
48                    style("Using Gemini 3.1 Pro model (auto-accept enabled)").cyan()
49                );
50                return Ok(requested_model.to_string());
51            }
52
53            if let Some(task) = task_description {
54                println!("{}", style("Model Selection Review").cyan().bold());
55                println!("Task: {}", style(task).cyan());
56                println!();
57            }
58
59            // Ask for explicit confirmation before using the most capable model
60            match UserConfirmation::confirm_pro_model_usage(current_default.as_str())? {
61                ProModelConfirmationResult::Yes => {}
62                ProModelConfirmationResult::YesAutoAccept => {
63                    PRO_MODEL_AUTO_ACCEPT.store(true, Ordering::Relaxed);
64                }
65                ProModelConfirmationResult::No => {
66                    println!(
67                        "Falling back to default model: {}",
68                        current_default.display_name()
69                    );
70                    return Ok(current_default.to_string());
71                }
72            }
73        }
74
75        Ok(requested_model.to_string())
76    }
77
78    /// Validate agent mode selection based on task complexity and user preferences
79    /// Returns the recommended agent mode with user confirmation if needed
80    pub fn validate_agent_mode(
81        _task_description: &str,
82        _skip_confirmations: bool,
83    ) -> Result<AgentMode> {
84        // Always use single-agent mode
85        println!(
86            "{}",
87            style("Using single-agent mode with Decision Ledger").green()
88        );
89        Ok(AgentMode::SingleCoder)
90    }
91
92    /// Check if a model switch is safe and cost-effective
93    pub fn is_model_switch_safe(from_model: &str, to_model: &str) -> bool {
94        use std::str::FromStr;
95        let from_id = ModelId::from_str(from_model).ok();
96        let to_id = ModelId::from_str(to_model).ok();
97
98        match (from_id, to_id) {
99            (Some(from), Some(to)) => {
100                // Switching to Pro model requires confirmation
101                !matches!(to, ModelId::Gemini31ProPreview)
102                    || matches!(from, ModelId::Gemini31ProPreview)
103            }
104            _ => true, // Unknown models are allowed
105        }
106    }
107
108    /// Display safety recommendations for the current configuration
109    pub fn display_safety_recommendations(
110        model: &str,
111        agent_mode: &AgentMode,
112        task_description: Option<&str>,
113    ) {
114        println!("{}", style(" Safety Configuration Summary").cyan().bold());
115        println!("Model: {}", style(model).green());
116        println!("Agent Mode: {}", style(format!("{:?}", agent_mode)).green());
117
118        if let Some(task) = task_description {
119            println!("Task: {}", style(task).cyan());
120        }
121
122        println!();
123
124        // Model-specific recommendations
125        use crate::config::constants::models;
126        match model {
127            s if s == models::google::GEMINI_3_FLASH_PREVIEW => {
128                println!("{}", style("[FAST] Using balanced model:").green());
129                println!("• Good quality responses");
130                println!("• Reasonable cost");
131                println!("• Fast response times");
132            }
133            s if s == models::google::GEMINI_3_1_PRO_PREVIEW => {
134                println!("{}", style("Using most capable model:").cyan());
135                println!("• Highest quality responses");
136                println!("• Higher cost per token");
137                println!("• Slower response times");
138            }
139            _ => {}
140        }
141
142        // Agent mode recommendations
143        match agent_mode {
144            AgentMode::SingleCoder => {
145                println!("{}", style("Single-Agent System:").cyan());
146                println!("• Streamlined execution");
147                println!("• Decision Ledger tracking");
148                println!("• Lower API costs");
149                println!("• Faster task completion");
150                println!("• Best for most development tasks");
151            }
152        }
153
154        println!();
155    }
156
157    /// Validate resource usage and warn about potential costs
158    pub fn validate_resource_usage(
159        model: &str,
160        _agent_mode: &AgentMode,
161        estimated_tokens: Option<usize>,
162    ) -> Result<bool> {
163        use crate::config::constants::models;
164        let mut warnings = Vec::new();
165
166        // Check for expensive model usage
167        if model == models::google::GEMINI_3_1_PRO_PREVIEW {
168            warnings.push("Using most expensive model (Gemini 3 Pro)");
169        }
170
171        // Single-agent mode uses standard resource usage
172
173        // Check for high token usage
174        if let Some(tokens) = estimated_tokens
175            && tokens > 10000
176        {
177            warnings.push("High token usage estimated (>10k tokens)");
178        }
179
180        if !warnings.is_empty() {
181            println!("{}", style(" Resource Usage Warning").red().bold());
182            for warning in &warnings {
183                println!("• {}", warning);
184            }
185            println!();
186
187            let confirmed = UserConfirmation::confirm_action(
188                "Do you want to proceed with these resource usage implications?",
189                false,
190            )?;
191
192            return Ok(confirmed);
193        }
194
195        Ok(true)
196    }
197}