mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
tui: queue smart approvals updates
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
co-authored by
Codex
parent
8d3ed74adc
commit
11d49d9259
+49
-14
@@ -2802,12 +2802,14 @@ impl App {
|
||||
}
|
||||
AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
} => {
|
||||
self.chat_widget.open_world_writable_warning_confirmation(
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
@@ -2827,10 +2829,17 @@ impl App {
|
||||
self.launch_external_editor(tui).await;
|
||||
}
|
||||
}
|
||||
AppEvent::OpenWindowsSandboxEnablePrompt { preset } => {
|
||||
self.chat_widget.open_windows_sandbox_enable_prompt(preset);
|
||||
AppEvent::OpenWindowsSandboxEnablePrompt {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
} => {
|
||||
self.chat_widget
|
||||
.open_windows_sandbox_enable_prompt(preset, approvals_reviewer);
|
||||
}
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt { preset } => {
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
} => {
|
||||
self.session_telemetry.counter(
|
||||
"codex.windows_sandbox.fallback_prompt_shown",
|
||||
1,
|
||||
@@ -2845,9 +2854,12 @@ impl App {
|
||||
);
|
||||
}
|
||||
self.chat_widget
|
||||
.open_windows_sandbox_fallback_prompt(preset);
|
||||
.open_windows_sandbox_fallback_prompt(preset, approvals_reviewer);
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxElevatedSetup { preset } => {
|
||||
AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let policy = preset.sandbox.clone();
|
||||
@@ -2865,6 +2877,7 @@ impl App {
|
||||
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode: WindowsSandboxEnableMode::Elevated,
|
||||
approvals_reviewer,
|
||||
});
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
@@ -2890,6 +2903,7 @@ impl App {
|
||||
AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset: preset.clone(),
|
||||
mode: WindowsSandboxEnableMode::Elevated,
|
||||
approvals_reviewer,
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -2921,7 +2935,10 @@ impl App {
|
||||
error = %err,
|
||||
"failed to run elevated Windows sandbox setup"
|
||||
);
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt { preset }
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
}
|
||||
}
|
||||
};
|
||||
tx.send(event);
|
||||
@@ -2929,10 +2946,13 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = preset;
|
||||
let _ = (preset, approvals_reviewer);
|
||||
}
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxLegacySetup { preset } => {
|
||||
AppEvent::BeginWindowsSandboxLegacySetup {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let policy = preset.sandbox.clone();
|
||||
@@ -2955,6 +2975,7 @@ impl App {
|
||||
let event = match result {
|
||||
Ok(()) => AppEvent::WindowsSandboxLegacySetupCompleted {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
error: None,
|
||||
},
|
||||
Err(err) => {
|
||||
@@ -2964,6 +2985,7 @@ impl App {
|
||||
);
|
||||
AppEvent::WindowsSandboxLegacySetupCompleted {
|
||||
preset: preset_for_error,
|
||||
approvals_reviewer,
|
||||
error: Some(err.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -2973,10 +2995,14 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = preset;
|
||||
let _ = (preset, approvals_reviewer);
|
||||
}
|
||||
}
|
||||
AppEvent::WindowsSandboxLegacySetupCompleted { preset, error } => {
|
||||
AppEvent::WindowsSandboxLegacySetupCompleted {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
error,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
match error {
|
||||
None => {
|
||||
@@ -2984,6 +3010,7 @@ impl App {
|
||||
.send(AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode: WindowsSandboxEnableMode::Legacy,
|
||||
approvals_reviewer,
|
||||
});
|
||||
}
|
||||
Some(err) => {
|
||||
@@ -2994,7 +3021,7 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = (preset, error);
|
||||
let _ = (preset, approvals_reviewer, error);
|
||||
}
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxGrantReadRoot { path } => {
|
||||
@@ -3054,7 +3081,11 @@ impl App {
|
||||
));
|
||||
}
|
||||
},
|
||||
AppEvent::EnableWindowsSandboxForAgentMode { preset, mode } => {
|
||||
AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode,
|
||||
approvals_reviewer,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
self.chat_widget.clear_windows_sandbox_setup_status();
|
||||
@@ -3110,6 +3141,7 @@ impl App {
|
||||
self.app_event_tx.send(
|
||||
AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: Some(preset.clone()),
|
||||
approvals_reviewer: Some(approvals_reviewer),
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
@@ -3120,7 +3152,7 @@ impl App {
|
||||
Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(preset.approval),
|
||||
approvals_reviewer: Some(self.config.approvals_reviewer),
|
||||
approvals_reviewer: Some(approvals_reviewer),
|
||||
sandbox_policy: Some(preset.sandbox.clone()),
|
||||
windows_sandbox_level: Some(windows_sandbox_level),
|
||||
model: None,
|
||||
@@ -3135,6 +3167,8 @@ impl App {
|
||||
.send(AppEvent::UpdateAskForApprovalPolicy(preset.approval));
|
||||
self.app_event_tx
|
||||
.send(AppEvent::UpdateSandboxPolicy(preset.sandbox.clone()));
|
||||
self.app_event_tx
|
||||
.send(AppEvent::UpdateApprovalsReviewer(approvals_reviewer));
|
||||
let _ = mode;
|
||||
self.chat_widget.add_plain_history_lines(vec![
|
||||
Line::from(vec!["• ".dim(), "Sandbox ready".into()]),
|
||||
@@ -3159,7 +3193,7 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = (preset, mode);
|
||||
let _ = (preset, mode, approvals_reviewer);
|
||||
}
|
||||
}
|
||||
AppEvent::PersistModelSelection { model, effort } => {
|
||||
@@ -4239,6 +4273,7 @@ impl App {
|
||||
// Scan failed: warn without examples.
|
||||
tx.send(AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: None,
|
||||
approvals_reviewer: None,
|
||||
sample_paths: Vec::new(),
|
||||
extra_count: 0usize,
|
||||
failed_scan: true,
|
||||
|
||||
@@ -268,6 +268,7 @@ pub(crate) enum AppEvent {
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
OpenWorldWritableWarningConfirmation {
|
||||
preset: Option<ApprovalPreset>,
|
||||
approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
/// Up to 3 sample world-writable directories to display in the warning.
|
||||
sample_paths: Vec<String>,
|
||||
/// If there are more than `sample_paths`, this carries the remaining count.
|
||||
@@ -280,30 +281,35 @@ pub(crate) enum AppEvent {
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
OpenWindowsSandboxEnablePrompt {
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
},
|
||||
|
||||
/// Open the Windows sandbox fallback prompt after declining or failing elevation.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
OpenWindowsSandboxFallbackPrompt {
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
},
|
||||
|
||||
/// Begin the elevated Windows sandbox setup flow.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
BeginWindowsSandboxElevatedSetup {
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
},
|
||||
|
||||
/// Begin the non-elevated Windows sandbox setup flow.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
BeginWindowsSandboxLegacySetup {
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
},
|
||||
|
||||
/// Result of the non-elevated Windows sandbox setup flow.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
WindowsSandboxLegacySetupCompleted {
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
error: Option<String>,
|
||||
},
|
||||
|
||||
@@ -325,6 +331,7 @@ pub(crate) enum AppEvent {
|
||||
EnableWindowsSandboxForAgentMode {
|
||||
preset: ApprovalPreset,
|
||||
mode: WindowsSandboxEnableMode,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
},
|
||||
|
||||
/// Update the Windows sandbox feature mode without changing approval presets.
|
||||
|
||||
+170
-24
@@ -4537,7 +4537,10 @@ impl ChatWidget {
|
||||
&[],
|
||||
);
|
||||
self.app_event_tx
|
||||
.send(AppEvent::BeginWindowsSandboxElevatedSetup { preset });
|
||||
.send(AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
approvals_reviewer: self.config.approvals_reviewer,
|
||||
});
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
@@ -4803,7 +4806,7 @@ impl ChatWidget {
|
||||
SlashCommand::Approvals | SlashCommand::Permissions => {
|
||||
let args = match SlashCommandInvocation::parse_args(
|
||||
&args_message.text,
|
||||
"Usage: /approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]",
|
||||
"Usage: /approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]",
|
||||
) {
|
||||
Ok(args) => args,
|
||||
Err(message) => {
|
||||
@@ -4818,7 +4821,7 @@ impl ChatWidget {
|
||||
return self.restore_invalid_inline_slash_command(
|
||||
cmd,
|
||||
args_message,
|
||||
"Usage: /approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]".to_string(),
|
||||
"Usage: /approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]".to_string(),
|
||||
);
|
||||
}
|
||||
let preset_id = args[0].as_str();
|
||||
@@ -4837,9 +4840,11 @@ impl ChatWidget {
|
||||
let mut remember_full_access = false;
|
||||
let mut confirm_world_writable = false;
|
||||
let mut remember_world_writable = false;
|
||||
let mut smart_approvals = false;
|
||||
let mut windows_sandbox_mode = None;
|
||||
for token in args.iter().skip(1) {
|
||||
match token.as_str() {
|
||||
"--smart-approvals" => smart_approvals = true,
|
||||
"--confirm-full-access" => confirm_full_access = true,
|
||||
"--remember-full-access" => remember_full_access = true,
|
||||
"--confirm-world-writable" => confirm_world_writable = true,
|
||||
@@ -4859,6 +4864,25 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
if smart_approvals && preset.id != "auto" {
|
||||
return self.restore_invalid_inline_slash_command(
|
||||
cmd,
|
||||
args_message,
|
||||
"Smart Approvals is only available for Default permissions.".to_string(),
|
||||
);
|
||||
}
|
||||
if smart_approvals && !self.config.features.enabled(Feature::GuardianApproval) {
|
||||
return self.restore_invalid_inline_slash_command(
|
||||
cmd,
|
||||
args_message,
|
||||
"Smart Approvals is not enabled in this session.".to_string(),
|
||||
);
|
||||
}
|
||||
let approvals_reviewer = if smart_approvals {
|
||||
ApprovalsReviewer::GuardianSubagent
|
||||
} else {
|
||||
ApprovalsReviewer::User
|
||||
};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let _ = windows_sandbox_mode;
|
||||
|
||||
@@ -4889,6 +4913,11 @@ impl ChatWidget {
|
||||
};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let label = preset.label.to_string();
|
||||
let label = if smart_approvals {
|
||||
"Smart Approvals".to_string()
|
||||
} else {
|
||||
label
|
||||
};
|
||||
|
||||
if preset.id == "full-access"
|
||||
&& !confirm_full_access
|
||||
@@ -4921,12 +4950,19 @@ impl ChatWidget {
|
||||
};
|
||||
match mode {
|
||||
WindowsSandboxEnableMode::Elevated => {
|
||||
self.app_event_tx
|
||||
.send(AppEvent::BeginWindowsSandboxElevatedSetup { preset });
|
||||
self.app_event_tx.send(
|
||||
AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
},
|
||||
);
|
||||
}
|
||||
WindowsSandboxEnableMode::Legacy => {
|
||||
self.app_event_tx
|
||||
.send(AppEvent::BeginWindowsSandboxLegacySetup { preset });
|
||||
.send(AppEvent::BeginWindowsSandboxLegacySetup {
|
||||
preset,
|
||||
approvals_reviewer,
|
||||
});
|
||||
}
|
||||
}
|
||||
return QueueReplayControl::Stop;
|
||||
@@ -4966,7 +5002,7 @@ impl ChatWidget {
|
||||
.send(AppEvent::CodexOp(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(preset.approval),
|
||||
approvals_reviewer: Some(ApprovalsReviewer::User),
|
||||
approvals_reviewer: Some(approvals_reviewer),
|
||||
sandbox_policy: Some(sandbox.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
@@ -4981,7 +5017,7 @@ impl ChatWidget {
|
||||
self.app_event_tx
|
||||
.send(AppEvent::UpdateSandboxPolicy(sandbox));
|
||||
self.app_event_tx
|
||||
.send(AppEvent::UpdateApprovalsReviewer(ApprovalsReviewer::User));
|
||||
.send(AppEvent::UpdateApprovalsReviewer(approvals_reviewer));
|
||||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(format!("Permissions updated to {label}"), None),
|
||||
)));
|
||||
@@ -5666,12 +5702,23 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
fn approval_preset_draft(preset_id: &str, flags: &[&str]) -> UserMessage {
|
||||
fn approval_preset_draft_for_reviewer(
|
||||
preset_id: &str,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
flags: &[&str],
|
||||
) -> UserMessage {
|
||||
let mut args = vec![preset_id.to_string()];
|
||||
if approvals_reviewer == ApprovalsReviewer::GuardianSubagent {
|
||||
args.push("--smart-approvals".to_string());
|
||||
}
|
||||
args.extend(flags.iter().map(|flag| (*flag).to_string()));
|
||||
SlashCommandInvocation::with_args(SlashCommand::Approvals, args).into_user_message()
|
||||
}
|
||||
|
||||
fn approval_preset_draft(preset_id: &str, flags: &[&str]) -> UserMessage {
|
||||
Self::approval_preset_draft_for_reviewer(preset_id, ApprovalsReviewer::User, flags)
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_os = "linux"), feature = "voice-input"))]
|
||||
fn settings_device_draft(kind: RealtimeAudioDeviceKind, name: Option<&str>) -> UserMessage {
|
||||
let kind_name = match kind {
|
||||
@@ -8464,6 +8511,7 @@ impl ChatWidget {
|
||||
vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::OpenWindowsSandboxEnablePrompt {
|
||||
preset: preset_clone.clone(),
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
});
|
||||
})]
|
||||
}
|
||||
@@ -8474,6 +8522,7 @@ impl ChatWidget {
|
||||
vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: Some(preset_clone.clone()),
|
||||
approvals_reviewer: Some(ApprovalsReviewer::User),
|
||||
sample_paths: sample_paths.clone(),
|
||||
extra_count,
|
||||
failed_scan,
|
||||
@@ -8504,6 +8553,63 @@ impl ChatWidget {
|
||||
|
||||
if guardian_approval_enabled {
|
||||
let guardian_preset = preset.clone();
|
||||
let guardian_actions: Vec<SelectionAction> = {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if WindowsSandboxLevel::from_config(&self.config)
|
||||
== WindowsSandboxLevel::Disabled
|
||||
{
|
||||
let preset_clone = guardian_preset.clone();
|
||||
if codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& codex_core::windows_sandbox::sandbox_setup_is_complete(
|
||||
self.config.codex_home.as_path(),
|
||||
)
|
||||
{
|
||||
Self::approval_preset_actions_for_reviewer(
|
||||
guardian_preset.id,
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&["--enable-windows-sandbox=elevated"],
|
||||
)
|
||||
} else {
|
||||
vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::OpenWindowsSandboxEnablePrompt {
|
||||
preset: preset_clone.clone(),
|
||||
approvals_reviewer: ApprovalsReviewer::GuardianSubagent,
|
||||
});
|
||||
})]
|
||||
}
|
||||
} else if let Some((sample_paths, extra_count, failed_scan)) =
|
||||
self.world_writable_warning_details()
|
||||
{
|
||||
let preset_clone = guardian_preset.clone();
|
||||
vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: Some(preset_clone.clone()),
|
||||
approvals_reviewer: Some(
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
),
|
||||
sample_paths: sample_paths.clone(),
|
||||
extra_count,
|
||||
failed_scan,
|
||||
});
|
||||
})]
|
||||
} else {
|
||||
Self::approval_preset_actions_for_reviewer(
|
||||
guardian_preset.id,
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Self::approval_preset_actions_for_reviewer(
|
||||
guardian_preset.id,
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
};
|
||||
items.push(SelectionItem {
|
||||
name: "Guardian Approvals".to_string(),
|
||||
description: Some(
|
||||
@@ -8516,11 +8622,7 @@ impl ChatWidget {
|
||||
current_sandbox,
|
||||
&preset,
|
||||
),
|
||||
actions: Self::approval_preset_actions_for_reviewer(
|
||||
guardian_preset.id,
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&[],
|
||||
),
|
||||
actions: guardian_actions,
|
||||
dismiss_on_select: true,
|
||||
disabled_reason: approval_disabled_reason
|
||||
.or_else(|| guardian_disabled_reason(true)),
|
||||
@@ -8593,6 +8695,18 @@ impl ChatWidget {
|
||||
})]
|
||||
}
|
||||
|
||||
fn approval_preset_actions_for_reviewer(
|
||||
preset_id: &'static str,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
flags: &'static [&'static str],
|
||||
) -> Vec<SelectionAction> {
|
||||
vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::HandleSlashCommandDraft(
|
||||
Self::approval_preset_draft_for_reviewer(preset_id, approvals_reviewer, flags),
|
||||
));
|
||||
})]
|
||||
}
|
||||
|
||||
fn preset_matches_current(
|
||||
current_approval: AskForApproval,
|
||||
current_sandbox: &SandboxPolicy,
|
||||
@@ -8727,6 +8841,7 @@ impl ChatWidget {
|
||||
pub(crate) fn open_world_writable_warning_confirmation(
|
||||
&mut self,
|
||||
preset: Option<ApprovalPreset>,
|
||||
approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
sample_paths: Vec<String>,
|
||||
extra_count: usize,
|
||||
failed_scan: bool,
|
||||
@@ -8776,12 +8891,19 @@ impl ChatWidget {
|
||||
// so downstream policy-change hooks don't re-trigger the warning.
|
||||
let accept_actions = preset
|
||||
.as_ref()
|
||||
.map(|preset| Self::approval_preset_actions(preset.id, &["--confirm-world-writable"]))
|
||||
.map(|preset| {
|
||||
Self::approval_preset_actions_for_reviewer(
|
||||
preset.id,
|
||||
approvals_reviewer.unwrap_or(ApprovalsReviewer::User),
|
||||
&["--confirm-world-writable"],
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let accept_and_remember_actions: Vec<SelectionAction> =
|
||||
if let Some(preset) = preset.as_ref() {
|
||||
Self::approval_preset_actions(
|
||||
Self::approval_preset_actions_for_reviewer(
|
||||
preset.id,
|
||||
approvals_reviewer.unwrap_or(ApprovalsReviewer::User),
|
||||
&["--confirm-world-writable", "--remember-world-writable"],
|
||||
)
|
||||
} else {
|
||||
@@ -8820,6 +8942,7 @@ impl ChatWidget {
|
||||
pub(crate) fn open_world_writable_warning_confirmation(
|
||||
&mut self,
|
||||
_preset: Option<ApprovalPreset>,
|
||||
_approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
_sample_paths: Vec<String>,
|
||||
_extra_count: usize,
|
||||
_failed_scan: bool,
|
||||
@@ -8827,7 +8950,11 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn open_windows_sandbox_enable_prompt(&mut self, preset: ApprovalPreset) {
|
||||
pub(crate) fn open_windows_sandbox_enable_prompt(
|
||||
&mut self,
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
) {
|
||||
use ratatui_macros::line;
|
||||
|
||||
if !codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED {
|
||||
@@ -8850,6 +8977,7 @@ impl ChatWidget {
|
||||
actions: vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::BeginWindowsSandboxLegacySetup {
|
||||
preset: preset_clone.clone(),
|
||||
approvals_reviewer,
|
||||
});
|
||||
})],
|
||||
dismiss_on_select: true,
|
||||
@@ -8898,8 +9026,9 @@ impl ChatWidget {
|
||||
actions: vec![Box::new(move |tx| {
|
||||
accept_otel.counter("codex.windows_sandbox.elevated_prompt_accept", 1, &[]);
|
||||
tx.send(AppEvent::HandleSlashCommandDraft(
|
||||
Self::approval_preset_draft(
|
||||
Self::approval_preset_draft_for_reviewer(
|
||||
preset.id,
|
||||
approvals_reviewer,
|
||||
&["--enable-windows-sandbox=elevated"],
|
||||
),
|
||||
));
|
||||
@@ -8913,8 +9042,9 @@ impl ChatWidget {
|
||||
actions: vec![Box::new(move |tx| {
|
||||
legacy_otel.counter("codex.windows_sandbox.elevated_prompt_use_legacy", 1, &[]);
|
||||
tx.send(AppEvent::HandleSlashCommandDraft(
|
||||
Self::approval_preset_draft(
|
||||
Self::approval_preset_draft_for_reviewer(
|
||||
legacy_preset.id,
|
||||
approvals_reviewer,
|
||||
&["--enable-windows-sandbox=legacy"],
|
||||
),
|
||||
));
|
||||
@@ -8944,10 +9074,19 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) fn open_windows_sandbox_enable_prompt(&mut self, _preset: ApprovalPreset) {}
|
||||
pub(crate) fn open_windows_sandbox_enable_prompt(
|
||||
&mut self,
|
||||
_preset: ApprovalPreset,
|
||||
_approvals_reviewer: ApprovalsReviewer,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn open_windows_sandbox_fallback_prompt(&mut self, preset: ApprovalPreset) {
|
||||
pub(crate) fn open_windows_sandbox_fallback_prompt(
|
||||
&mut self,
|
||||
preset: ApprovalPreset,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
) {
|
||||
use ratatui_macros::line;
|
||||
|
||||
let mut lines = Vec::new();
|
||||
@@ -8978,8 +9117,9 @@ impl ChatWidget {
|
||||
move |tx| {
|
||||
otel.counter("codex.windows_sandbox.fallback_retry_elevated", 1, &[]);
|
||||
tx.send(AppEvent::HandleSlashCommandDraft(
|
||||
Self::approval_preset_draft(
|
||||
Self::approval_preset_draft_for_reviewer(
|
||||
preset.id,
|
||||
approvals_reviewer,
|
||||
&["--enable-windows-sandbox=elevated"],
|
||||
),
|
||||
));
|
||||
@@ -8997,8 +9137,9 @@ impl ChatWidget {
|
||||
move |tx| {
|
||||
otel.counter("codex.windows_sandbox.fallback_use_legacy", 1, &[]);
|
||||
tx.send(AppEvent::HandleSlashCommandDraft(
|
||||
Self::approval_preset_draft(
|
||||
Self::approval_preset_draft_for_reviewer(
|
||||
preset.id,
|
||||
approvals_reviewer,
|
||||
&["--enable-windows-sandbox=legacy"],
|
||||
),
|
||||
));
|
||||
@@ -9029,7 +9170,12 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) fn open_windows_sandbox_fallback_prompt(&mut self, _preset: ApprovalPreset) {}
|
||||
pub(crate) fn open_windows_sandbox_fallback_prompt(
|
||||
&mut self,
|
||||
_preset: ApprovalPreset,
|
||||
_approvals_reviewer: ApprovalsReviewer,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn maybe_prompt_windows_sandbox_enable(&mut self, show_now: bool) {
|
||||
|
||||
+6
-6
@@ -29,17 +29,17 @@ expression: popup
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/approvals
|
||||
/approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/permissions
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/permissions
|
||||
/permissions <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/permissions <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/experimental
|
||||
toggle experimental features
|
||||
|
||||
+6
-6
@@ -29,17 +29,17 @@ expression: popup
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/approvals
|
||||
/approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/permissions
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/permissions
|
||||
/permissions <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/permissions <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/setup-default-sandbox
|
||||
set up elevated agent sandbox
|
||||
|
||||
+6
-6
@@ -29,17 +29,17 @@ expression: searching
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/approvals
|
||||
/approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/permissions
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/permissions
|
||||
/permissions <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/permissions <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/experimental
|
||||
toggle experimental features
|
||||
|
||||
+6
-6
@@ -29,17 +29,17 @@ expression: searching
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/approvals
|
||||
/approvals <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/approvals <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/permissions
|
||||
choose what Codex is allowed to do
|
||||
Usage:
|
||||
/permissions
|
||||
/permissions <read-only|auto|full-access> [--confirm-full-access] [--remember-full-access]
|
||||
[--confirm-world-writable] [--remember-world-writable] [--enable-windows-
|
||||
sandbox=elevated|legacy]
|
||||
/permissions <read-only|auto|full-access> [--smart-approvals] [--confirm-full-access]
|
||||
[--remember-full-access] [--confirm-world-writable] [--remember-world-writable]
|
||||
[--enable-windows-sandbox=elevated|legacy]
|
||||
|
||||
/setup-default-sandbox
|
||||
set up elevated agent sandbox
|
||||
|
||||
@@ -9185,7 +9185,10 @@ async fn approvals_elevated_inline_args_start_windows_sandbox_setup() {
|
||||
|
||||
assert_matches!(
|
||||
rx.try_recv(),
|
||||
Ok(AppEvent::BeginWindowsSandboxElevatedSetup { preset }) if preset.id == "auto"
|
||||
Ok(AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
}) if preset.id == "auto"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9201,7 +9204,31 @@ async fn approvals_legacy_inline_args_start_windows_sandbox_setup() {
|
||||
|
||||
assert_matches!(
|
||||
rx.try_recv(),
|
||||
Ok(AppEvent::BeginWindowsSandboxLegacySetup { preset }) if preset.id == "auto"
|
||||
Ok(AppEvent::BeginWindowsSandboxLegacySetup {
|
||||
preset,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
}) if preset.id == "auto"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[tokio::test]
|
||||
async fn approvals_smart_inline_args_preserve_guardian_reviewer_through_windows_setup() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.set_feature_enabled(Feature::GuardianApproval, true);
|
||||
chat.set_windows_sandbox_mode(None);
|
||||
chat.handle_serialized_slash_command(ChatWidget::approval_preset_draft_for_reviewer(
|
||||
"auto",
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&["--enable-windows-sandbox=elevated"],
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
rx.try_recv(),
|
||||
Ok(AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
approvals_reviewer: ApprovalsReviewer::GuardianSubagent,
|
||||
}) if preset.id == "auto"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9583,7 +9610,7 @@ async fn permissions_selection_can_disable_guardian_approvals() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context() {
|
||||
async fn permissions_selection_emits_smart_approvals_draft_before_replay() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -9623,12 +9650,26 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context
|
||||
);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let draft = match rx.try_recv() {
|
||||
Ok(AppEvent::HandleSlashCommandDraft(draft)) => draft,
|
||||
other => panic!("expected serialized smart approvals draft, got {other:?}"),
|
||||
};
|
||||
assert_eq!(
|
||||
draft,
|
||||
ChatWidget::approval_preset_draft_for_reviewer(
|
||||
"auto",
|
||||
ApprovalsReviewer::GuardianSubagent,
|
||||
&[],
|
||||
)
|
||||
);
|
||||
chat.handle_serialized_slash_command(draft);
|
||||
|
||||
let op = std::iter::from_fn(|| rx.try_recv().ok())
|
||||
.find_map(|event| match event {
|
||||
AppEvent::CodexOp(op @ Op::OverrideTurnContext { .. }) => Some(op),
|
||||
_ => None,
|
||||
})
|
||||
.expect("expected OverrideTurnContext op");
|
||||
.expect("expected OverrideTurnContext op after replay");
|
||||
|
||||
assert_eq!(
|
||||
op,
|
||||
|
||||
@@ -187,7 +187,7 @@ impl SlashCommand {
|
||||
SlashCommand::Fast => &["", "<on|off|status>"],
|
||||
SlashCommand::Approvals | SlashCommand::Permissions => &[
|
||||
"",
|
||||
"<read-only|auto|full-access> [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]",
|
||||
"<read-only|auto|full-access> [--smart-approvals] [--confirm-full-access] [--remember-full-access] [--confirm-world-writable] [--remember-world-writable] [--enable-windows-sandbox=elevated|legacy]",
|
||||
],
|
||||
SlashCommand::ElevateSandbox => &[""],
|
||||
SlashCommand::SandboxReadRoot => &["<absolute-directory-path>"],
|
||||
|
||||
Reference in New Issue
Block a user