diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 1c6bf95bf3..195ae2e1af 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5283,6 +5283,7 @@ impl ChatWidget { let current_sandbox = self.config.permissions.sandbox_policy.get(); let mut items: Vec = Vec::new(); let presets: Vec = builtin_approval_presets(); + let mut displayed_presets: Vec = Vec::new(); #[cfg(target_os = "windows")] let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config); @@ -5300,6 +5301,7 @@ impl ChatWidget { if !include_read_only && preset.id == "read-only" { continue; } + displayed_presets.push(preset.clone()); let is_current = Self::preset_matches_current(current_approval, current_sandbox, &preset); let name = if preset.id == "auto" && windows_degraded_sandbox_enabled { @@ -5392,20 +5394,27 @@ impl ChatWidget { let custom_config_permissions = Self::custom_permissions_from_user_config(&self.config); if let Some((approval, sandbox)) = custom_config_permissions { - let is_current = current_approval == approval && *current_sandbox == sandbox; - let disabled_reason = match self.config.permissions.approval_policy.can_set(&approval) { - Ok(()) => None, - Err(err) => Some(err.to_string()), - }; - items.push(SelectionItem { - name: "Custom".to_string(), - description: Some(crate::status::permissions_display_text(&self.config)), - is_current, - actions: Self::approval_preset_actions(approval, sandbox), - dismiss_on_select: true, - disabled_reason, - ..Default::default() - }); + let matches_displayed_preset = displayed_presets + .iter() + .any(|preset| preset.approval == approval && preset.sandbox == sandbox); + if !matches_displayed_preset { + let is_current = current_approval == approval && *current_sandbox == sandbox; + let disabled_reason = + match self.config.permissions.approval_policy.can_set(&approval) { + Ok(()) => None, + Err(err) => Some(err.to_string()), + }; + let description = crate::status::permissions_display_text_for(approval, &sandbox); + items.push(SelectionItem { + name: "Custom".to_string(), + description: Some(description), + is_current, + actions: Self::approval_preset_actions(approval, sandbox), + dismiss_on_select: true, + disabled_reason, + ..Default::default() + }); + } } let footer_note = show_elevate_sandbox_hint.then(|| { diff --git a/codex-rs/tui/src/status/card.rs b/codex-rs/tui/src/status/card.rs index e61bf18f40..c96e5661ee 100644 --- a/codex-rs/tui/src/status/card.rs +++ b/codex-rs/tui/src/status/card.rs @@ -7,7 +7,7 @@ use chrono::DateTime; use chrono::Local; use codex_core::WireApi; use codex_core::config::Config; -use codex_core::protocol::NetworkAccess; +use codex_core::protocol::AskForApproval; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; use codex_core::protocol::TokenUsageInfo; @@ -366,8 +366,18 @@ impl StatusHistoryCell { } pub(crate) fn permissions_display_text(config: &Config) -> String { - let approval = config.permissions.approval_policy.value().to_string(); - let sandbox = summarize_sandbox_policy(config.permissions.sandbox_policy.get(), true); + permissions_display_text_for( + config.permissions.approval_policy.value(), + config.permissions.sandbox_policy.get(), + ) +} + +pub(crate) fn permissions_display_text_for( + approval: AskForApproval, + sandbox_policy: &SandboxPolicy, +) -> String { + let approval = approval.to_string(); + let sandbox = summarize_sandbox_policy(sandbox_policy, true); match (approval.as_str(), sandbox.as_str()) { ("on-request", "workspace-write") => "Default".to_string(), ("never", "danger-full-access") => "Full Access".to_string(), diff --git a/codex-rs/tui/src/status/mod.rs b/codex-rs/tui/src/status/mod.rs index b37ca43b70..f0cbfdfead 100644 --- a/codex-rs/tui/src/status/mod.rs +++ b/codex-rs/tui/src/status/mod.rs @@ -15,7 +15,7 @@ mod rate_limits; #[cfg(test)] pub(crate) use card::new_status_output; pub(crate) use card::new_status_output_with_rate_limits; -pub(crate) use card::permissions_display_text; +pub(crate) use card::permissions_display_text_for; pub(crate) use helpers::format_directory_display; pub(crate) use helpers::format_tokens_compact; pub(crate) use rate_limits::RateLimitSnapshotDisplay; diff --git a/codex-rs/utils/sandbox-summary/src/sandbox_summary.rs b/codex-rs/utils/sandbox-summary/src/sandbox_summary.rs index ccb3f68062..d54dbf5ec6 100644 --- a/codex-rs/utils/sandbox-summary/src/sandbox_summary.rs +++ b/codex-rs/utils/sandbox-summary/src/sandbox_summary.rs @@ -38,12 +38,12 @@ pub fn summarize_sandbox_policy( .map(|p| p.to_string_lossy().to_string()), ); + if *network_access { + summary.push_str(" with network access"); + } if !ignore_writable_roots { summary.push_str(&format!(" [{}]", writable_entries.join(", "))); } - if *network_access { - summary.push_str(" (network access enabled)"); - } summary } }