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!(
46            "Requested model: {}",
47            style(models::google::GEMINI_3_1_PRO_PREVIEW).cyan().bold()
48        );
49        println!();
50        println!("The Gemini 3 Pro model is the most capable but also:");
51        println!("• More expensive per token");
52        println!("• Slower response times");
53        println!("• Higher resource usage");
54        println!();
55
56        let options = vec![
57            "Yes - Use Pro model for this task",
58            "Yes - Always use Pro model (Auto-accept)",
59            "No - Use default model instead",
60        ];
61
62        let selection = Select::new()
63            .with_prompt("How would you like to proceed?")
64            .default(0)
65            .items(&options)
66            .interact()?;
67
68        match selection {
69            0 => {
70                println!(
71                    "{}",
72                    style("✓ Using Gemini 3 Pro model for this task").green()
73                );
74                Ok(ProModelConfirmationResult::Yes)
75            }
76            1 => {
77                println!(
78                    "{}",
79                    style("✓ Using Gemini 3 Pro model (will auto-accept in future)").green()
80                );
81                Ok(ProModelConfirmationResult::YesAutoAccept)
82            }
83            2 => {
84                println!("{}", style("✗ Keeping current model").red());
85                Ok(ProModelConfirmationResult::No)
86            }
87            _ => Ok(ProModelConfirmationResult::No),
88        }
89    }
90
91    /// Present agent mode selection options to the user
92    pub fn select_agent_mode() -> Result<AgentMode> {
93        println!("{}", style("Agent Mode Selection").cyan().bold());
94        println!(
95            "VT Code now uses single-agent mode with Decision Ledger for reliable task execution."
96        );
97
98        Ok(AgentMode::SingleCoder)
99    }
100
101    /// Ask for task complexity assessment to determine agent mode
102    pub fn assess_task_complexity(task_description: &str) -> Result<TaskComplexity> {
103        println!("{}", style("Task Complexity Assessment").cyan().bold());
104        println!("Task: {}", style(task_description).cyan());
105        println!();
106
107        let options = vec![
108            "Simple (single file edit, basic question, straightforward task)",
109            "Moderate (multiple files, refactoring, testing)",
110            "Complex (architecture changes, cross-cutting concerns, large refactoring)",
111        ];
112
113        let selection = Select::new()
114            .with_prompt("How would you classify this task's complexity?")
115            .default(0)
116            .items(&options)
117            .interact()?;
118
119        let complexity = match selection {
120            0 => TaskComplexity::Simple,
121            1 => TaskComplexity::Moderate,
122            2 => TaskComplexity::Complex,
123            _ => TaskComplexity::Simple, // Default fallback
124        };
125
126        match complexity {
127            TaskComplexity::Simple => {
128                println!(
129                    "{}",
130                    style("Simple task - Single agent recommended").green()
131                );
132            }
133            TaskComplexity::Moderate => {
134                println!(
135                    "{}",
136                    style("Moderate task - Single agent usually sufficient").cyan()
137                );
138            }
139            TaskComplexity::Complex => {
140                println!(
141                    "{}",
142                    style("Complex task detected - proceeding with single-agent mode").cyan()
143                );
144            }
145        }
146
147        Ok(complexity)
148    }
149
150    /// Simple yes/no confirmation with custom message
151    #[must_use = "confirmation action failure is silently ignored"]
152    pub fn confirm_action(message: &str, default: bool) -> Result<bool> {
153        Confirm::new()
154            .with_prompt(message)
155            .default(default)
156            .interact()
157            .map_err(Into::into)
158    }
159
160    /// Display a warning message and wait for user acknowledgment
161    #[cold]
162    pub fn show_warning(message: &str) -> Result<()> {
163        println!("{}", style(" Warning").red().bold());
164        println!("{}", message);
165        println!();
166
167        Confirm::new()
168            .with_prompt("Press Enter to continue or Ctrl+C to cancel")
169            .default(true)
170            .interact()?;
171
172        Ok(())
173    }
174
175    /// Ask for detailed confirmation for tool usage
176    pub fn confirm_tool_usage(
177        tool_name: &str,
178        tool_args: Option<&str>,
179    ) -> Result<ToolConfirmationResult> {
180        println!("{}", style("Tool Execution Confirmation").cyan().bold());
181        println!("Tool: {}", style(tool_name).cyan().bold());
182        if let Some(args) = tool_args {
183            println!("Args: {}", style(args).dim());
184        }
185        println!();
186
187        let options = vec![
188            "Yes - Allow this execution",
189            "Yes - Always allow this tool (Auto-accept)",
190            "No - Deny this execution",
191            "No - Deny and provide feedback to agent",
192        ];
193
194        let selection = Select::new()
195            .with_prompt("How would you like to proceed?")
196            .default(0)
197            .items(&options)
198            .interact()?;
199
200        match selection {
201            0 => Ok(ToolConfirmationResult::Yes),
202            1 => Ok(ToolConfirmationResult::YesAutoAccept),
203            2 => Ok(ToolConfirmationResult::No),
204            3 => {
205                let feedback: String = Input::new()
206                    .with_prompt("Enter feedback for the agent")
207                    .allow_empty(false)
208                    .interact_text()?;
209                Ok(ToolConfirmationResult::Feedback(feedback))
210            }
211            _ => Ok(ToolConfirmationResult::No),
212        }
213    }
214}
215
216/// Available agent modes
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum AgentMode {
219    /// Single coder agent with Decision Ledger - reliable for all tasks
220    SingleCoder,
221}
222
223/// Task complexity levels for agent mode selection
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
225pub enum TaskComplexity {
226    /// Simple tasks - single file edits, basic questions
227    Simple,
228    /// Moderate tasks - multiple files, refactoring
229    Moderate,
230    /// Complex tasks - architecture changes, large refactoring
231    Complex,
232}
233
234impl TaskComplexity {
235    /// Recommend agent mode based on task complexity
236    pub fn recommended_agent_mode(&self) -> AgentMode {
237        match self {
238            TaskComplexity::Simple | TaskComplexity::Moderate => AgentMode::SingleCoder,
239            TaskComplexity::Complex => AgentMode::SingleCoder, // Default to SingleCoder as MultiAgent is removed
240        }
241    }
242}