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