tui: queue smart approvals updates

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Charles Cunningham
2026-03-15 23:36:33 -07:00
co-authored by Codex
parent 8d3ed74adc
commit 11d49d9259
9 changed files with 296 additions and 67 deletions
+49 -14
View File
@@ -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,
+7
View File
@@ -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
View File
@@ -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) {
@@ -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
@@ -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
@@ -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
@@ -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
+45 -4
View File
@@ -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,
+1 -1
View File
@@ -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>"],