diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs index 83afdca30c..820d4b8ca3 100644 --- a/codex-rs/tui/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui/src/bottom_pane/command_popup.rs @@ -302,6 +302,7 @@ mod tests { cmds, vec![ "model", + "permissions", "experimental", "resume", "compact", diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__slash_popup_mo.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__slash_popup_mo.snap index 661e82e3ad..d3e107a49a 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__slash_popup_mo.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__slash_popup_mo.snap @@ -5,5 +5,5 @@ expression: terminal.backend() " " "› /mo " " " -" /model choose what model and reasoning effort to use " -" /mention mention a file " +" /model choose what model and reasoning effort to " +" use " diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 48ed806d3a..fb4ae9996c 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2254,6 +2254,9 @@ impl ChatWidget { SlashCommand::Approvals => { self.open_approvals_popup(); } + SlashCommand::Permissions => { + self.open_permissions_popup(); + } SlashCommand::ElevateSandbox => { #[cfg(target_os = "windows")] { @@ -3519,6 +3522,42 @@ impl ChatWidget { /// Open a popup to choose the approvals mode (ask for approval policy + sandbox policy). pub(crate) fn open_approvals_popup(&mut self) { + self.open_approval_mode_popup( + "Update Model Permissions", + true, + |preset, is_windows_degraded| { + if preset.id == "auto" && is_windows_degraded { + "Agent (non-elevated sandbox)".to_string() + } else { + preset.label.to_string() + } + }, + ); + } + + /// Open a popup to choose the permissions mode (approval policy + sandbox policy). + pub(crate) fn open_permissions_popup(&mut self) { + let include_read_only = cfg!(target_os = "windows"); + self.open_approval_mode_popup( + "Update Model Permissions", + include_read_only, + |preset, _| match preset.id { + "read-only" => "Read Only".to_string(), + "auto" => "Default".to_string(), + "full-access" => "Full Access".to_string(), + _ => preset.label.to_string(), + }, + ); + } + + fn open_approval_mode_popup( + &mut self, + title: &str, + include_read_only: bool, + label_for_preset: F, + ) where + F: Fn(&ApprovalPreset, bool) -> String, + { let current_approval = self.config.approval_policy.value(); let current_sandbox = self.config.sandbox_policy.get(); let mut items: Vec = Vec::new(); @@ -3535,13 +3574,12 @@ impl ChatWidget { && presets.iter().any(|preset| preset.id == "auto"); for preset in presets.into_iter() { + if !include_read_only && preset.id == "read-only" { + continue; + } let is_current = Self::preset_matches_current(current_approval, current_sandbox, &preset); - let name = if preset.id == "auto" && windows_degraded_sandbox_enabled { - "Agent (non-elevated sandbox)".to_string() - } else { - preset.label.to_string() - }; + let name = label_for_preset(&preset, windows_degraded_sandbox_enabled); let description = Some(preset.description.to_string()); let disabled_reason = match self.config.approval_policy.can_set(&preset.approval) { Ok(()) => None, @@ -3627,7 +3665,7 @@ impl ChatWidget { }); self.bottom_pane.show_selection_view(SelectionViewParams { - title: Some("Select Approval Mode".to_string()), + title: Some(title.to_string()), footer_note, footer_hint: Some(standard_popup_hint_line()), items, diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__approvals_selection_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__approvals_selection_popup.snap index 6758ec62c5..044d16f9d4 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__approvals_selection_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__approvals_selection_popup.snap @@ -2,7 +2,7 @@ source: tui/src/chatwidget/tests.rs expression: popup --- - Select Approval Mode + Update Model Permissions › 1. Read Only (current) Requires approval to edit files and run commands. 2. Agent Read and edit files, and run commands. diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 46f5c3be9e..f114428c23 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -3010,7 +3010,7 @@ async fn approvals_popup_navigation_skips_disabled() { .expect("render approvals popup after disabled selection"); let screen = terminal.backend().vt100().screen().contents(); assert!( - screen.contains("Select Approval Mode"), + screen.contains("Update Model Permissions"), "popup should remain open after selecting a disabled entry" ); assert!( diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 157ccb97a2..2355a52568 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -947,6 +947,11 @@ pub(crate) fn new_session_info( "/approvals".into(), " - choose what Codex can do without approval".dim(), ]), + Line::from(vec![ + " ".into(), + "/permissions".into(), + " - choose what Codex is allowed to do".dim(), + ]), Line::from(vec![ " ".into(), "/model".into(), diff --git a/codex-rs/tui/src/slash_command.rs b/codex-rs/tui/src/slash_command.rs index ab63d6c7c8..f825f32fa8 100644 --- a/codex-rs/tui/src/slash_command.rs +++ b/codex-rs/tui/src/slash_command.rs @@ -14,6 +14,7 @@ pub enum SlashCommand { // more frequently used commands should be listed first. Model, Approvals, + Permissions, #[strum(serialize = "setup-elevated-sandbox")] ElevateSandbox, Experimental, @@ -60,6 +61,7 @@ impl SlashCommand { SlashCommand::Model => "choose what model and reasoning effort to use", SlashCommand::Collab => "change collaboration mode (experimental)", SlashCommand::Approvals => "choose what Codex can do without approval", + SlashCommand::Permissions => "choose what Codex is allowed to do", SlashCommand::ElevateSandbox => "set up elevated agent sandbox", SlashCommand::Experimental => "toggle beta features", SlashCommand::Mcp => "list configured MCP tools", @@ -86,6 +88,7 @@ impl SlashCommand { // | SlashCommand::Undo | SlashCommand::Model | SlashCommand::Approvals + | SlashCommand::Permissions | SlashCommand::ElevateSandbox | SlashCommand::Experimental | SlashCommand::Review