Skip to main content

vtcode_core/ui/
user_confirmation.rs

1//! User confirmation utilities for safety-critical operations
2//!
3//! This module provides utilities for asking user confirmation before
4//! performing operations that may be expensive or require explicit consent.
5
6use crate::utils::colors::style;
7use anyhow::Result;
8use dialoguer::{Confirm, Input, Select};
9// use std::io::Write;
10
11/// User confirmation utilities for safety-critical operations
12pub struct UserConfirmation;
13
14/// Result of a tool confirmation prompt
15#[derive(Debug, Clone, PartialEq)]
16pub enum ToolConfirmationResult {
17    /// Allow this specific execution
18    Yes,
19    /// Allow this and all future executions of this tool
20    YesAutoAccept,
21    /// Deny this execution
22    No,
23    /// Deny and provide feedback to the agent
24    Feedback(String),
25}
26
27/// Result of prompting for Gemini Pro model usage
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ProModelConfirmationResult {
30    /// Approve for this invocation only
31    Yes,
32    /// Approve this and future invocations in the current process
33    YesAutoAccept,
34    /// Deny and fallback to default model
35    No,
36}
37
38impl UserConfirmation {
39    /// Ask for confirmation before switching to the most capable model (Gemini 3 Pro)
40    /// This is critical for ensuring user control over potentially expensive operations
41    pub fn confirm_pro_model_usage(current_model: &str) -> Result<ProModelConfirmationResult> {
42        use crate::config::constants::models;
43        println!("{}", style("Model Upgrade Required").red().bold());
44        println!("Current model: {}", style(current_model).cyan());
45        println!("Requested model: {}", style(models::google::GEMINI_3_7_FLASH).cyan().bold());
46        println!();
47        println!("The Gemini 3 Pro model is the most capable but also:");
48        println!("• More expensive per token");
49        println!("• Slower response times");
50        println!("• Higher resource usage");
51        println!();
52
53        let options = vec![
54            "Yes - Use Pro model for this task",
55            "Yes - Always use Pro model (Auto-accept)",
56            "No - Use default model instead",
57        ];
58
59        let selection = Select::new()
60            .with_prompt("How would you like to proceed?")
61            .default(0)
62            .items(&options)
63            .interact()?;
64
65        match selection {
66            0 => {
67                println!("{}", style("✓ Using Gemini 3 Pro model for this task").green());
68                Ok(ProModelConfirmationResult::Yes)
69            }
70            1 => {
71                println!("{}", style("✓ Using Gemini 3 Pro model (will auto-accept in future)").green());
72                Ok(ProModelConfirmationResult::YesAutoAccept)
73            }
74            2 => {
75                println!("{}", style("✗ Keeping current model").red());
76                Ok(ProModelConfirmationResult::No)
77            }
78            _ => Ok(ProModelConfirmationResult::No),
79        }
80    }
81
82    /// Present agent mode selection options to the user
83    pub fn select_agent_mode() -> Result<AgentMode> {
84        println!("{}", style("Agent Mode Selection").cyan().bold());
85        println!("VT Code now uses single-agent mode with Decision Ledger for reliable task execution.");
86
87        Ok(AgentMode::SingleCoder)
88    }
89
90    /// Ask for task complexity assessment to determine agent mode
91    pub fn assess_task_complexity(task_description: &str) -> Result<TaskComplexity> {
92        println!("{}", style("Task Complexity Assessment").cyan().bold());
93        println!("Task: {}", style(task_description).cyan());
94        println!();
95
96        let options = vec![
97            "Simple (single file edit, basic question, straightforward task)",
98            "Moderate (multiple files, refactoring, testing)",
99            "Complex (architecture changes, cross-cutting concerns, large refactoring)",
100        ];
101
102        let selection = Select::new()
103            .with_prompt("How would you classify this task's complexity?")
104            .default(0)
105            .items(&options)
106            .interact()?;
107
108        let complexity = match selection {
109            0 => TaskComplexity::Simple,
110            1 => TaskComplexity::Moderate,
111            2 => TaskComplexity::Complex,
112            _ => TaskComplexity::Simple, // Default fallback
113        };
114
115        match complexity {
116            TaskComplexity::Simple => {
117                println!("{}", style("Simple task - Single agent recommended").green());
118            }
119            TaskComplexity::Moderate => {
120                println!("{}", style("Moderate task - Single agent usually sufficient").cyan());
121            }
122            TaskComplexity::Complex => {
123                println!("{}", style("Complex task detected - proceeding with single-agent mode").cyan());
124            }
125        }
126
127        Ok(complexity)
128    }
129
130    /// Simple yes/no confirmation with custom message
131    #[must_use = "confirmation action failure is silently ignored"]
132    pub fn confirm_action(message: &str, default: bool) -> Result<bool> {
133        Confirm::new()
134            .with_prompt(message)
135            .default(default)
136            .interact()
137            .map_err(Into::into)
138    }
139
140    /// Display a warning message and wait for user acknowledgment
141    #[cold]
142    pub fn show_warning(message: &str) -> Result<()> {
143        println!("{}", style(" Warning").red().bold());
144        println!("{message}");
145        println!();
146
147        Confirm::new()
148            .with_prompt("Press Enter to continue or Ctrl+C to cancel")
149            .default(true)
150            .interact()?;
151
152        Ok(())
153    }
154
155    /// Ask for detailed confirmation for tool usage
156    pub fn confirm_tool_usage(tool_name: &str, tool_args: Option<&str>) -> Result<ToolConfirmationResult> {
157        println!("{}", style("Tool Execution Confirmation").cyan().bold());
158        println!("Tool: {}", style(tool_name).cyan().bold());
159        if let Some(args) = tool_args {
160            println!("Args: {}", style(args).dim());
161        }
162        println!();
163
164        let options = vec![
165            "Yes - Allow this execution",
166            "Yes - Always allow this tool (Auto-accept)",
167            "No - Deny this execution",
168            "No - Deny and provide feedback to agent",
169        ];
170
171        let selection = Select::new()
172            .with_prompt("How would you like to proceed?")
173            .default(0)
174            .items(&options)
175            .interact()?;
176
177        match selection {
178            0 => Ok(ToolConfirmationResult::Yes),
179            1 => Ok(ToolConfirmationResult::YesAutoAccept),
180            2 => Ok(ToolConfirmationResult::No),
181            3 => {
182                let feedback: String = Input::new()
183                    .with_prompt("Enter feedback for the agent")
184                    .allow_empty(false)
185                    .interact_text()?;
186                Ok(ToolConfirmationResult::Feedback(feedback))
187            }
188            _ => Ok(ToolConfirmationResult::No),
189        }
190    }
191}
192
193/// Available agent modes
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum AgentMode {
196    /// Single coder agent with Decision Ledger - reliable for all tasks
197    SingleCoder,
198}
199
200/// Task complexity levels for agent mode selection
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum TaskComplexity {
203    /// Simple tasks - single file edits, basic questions
204    Simple,
205    /// Moderate tasks - multiple files, refactoring
206    Moderate,
207    /// Complex tasks - architecture changes, large refactoring
208    Complex,
209}
210
211impl TaskComplexity {
212    /// Recommend agent mode based on task complexity
213    pub fn recommended_agent_mode(&self) -> AgentMode {
214        match self {
215            TaskComplexity::Simple | TaskComplexity::Moderate => AgentMode::SingleCoder,
216            TaskComplexity::Complex => AgentMode::SingleCoder, // Default to SingleCoder as MultiAgent is removed
217        }
218    }
219}