From 035b1b803077cbda5acbab5b17ee1f9b84838936 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Thu, 30 Apr 2026 06:07:05 -0700 Subject: [PATCH] tui: centralize legacy sandbox projection --- codex-rs/tui/src/app/event_dispatch.rs | 32 ++++++------------------ codex-rs/tui/src/app/platform_actions.rs | 8 +++--- codex-rs/tui/src/app_command.rs | 18 ++++--------- codex-rs/tui/src/app_server_session.rs | 12 ++------- codex-rs/tui/src/chatwidget.rs | 13 ++++------ codex-rs/tui/src/lib.rs | 7 +++--- codex-rs/tui/src/permission_compat.rs | 12 +++++++++ 7 files changed, 40 insertions(+), 62 deletions(-) diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 545c9bd6d8..31e2cb4557 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -746,18 +746,10 @@ impl App { self.chat_widget.show_windows_sandbox_setup_status(); self.windows_sandbox.setup_started_at = Some(Instant::now()); let session_telemetry = self.session_telemetry.clone(); - let Ok(policy) = permission_profile - .to_legacy_sandbox_policy(policy_cwd.as_path()) - .inspect_err(|err| { - tracing::error!( - %err, - "approval preset permissions cannot be projected for elevated Windows sandbox setup" - ); - }) - else { - tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt { preset }); - return Ok(AppRunControl::Continue); - }; + let policy = crate::permission_compat::legacy_compatible_sandbox_policy( + &permission_profile, + policy_cwd.as_path(), + ); tokio::task::spawn_blocking(move || { let result = crate::legacy_core::windows_sandbox::run_elevated_setup( &policy, @@ -831,18 +823,10 @@ impl App { let session_telemetry = self.session_telemetry.clone(); self.chat_widget.show_windows_sandbox_setup_status(); - let Ok(policy) = permission_profile - .to_legacy_sandbox_policy(policy_cwd.as_path()) - .inspect_err(|err| { - tracing::error!( - %err, - "approval preset permissions cannot be projected for legacy Windows sandbox setup" - ); - }) - else { - tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt { preset }); - return Ok(AppRunControl::Continue); - }; + let policy = crate::permission_compat::legacy_compatible_sandbox_policy( + &permission_profile, + policy_cwd.as_path(), + ); tokio::task::spawn_blocking(move || { if let Err(err) = crate::legacy_core::windows_sandbox::run_legacy_setup_preflight( diff --git a/codex-rs/tui/src/app/platform_actions.rs b/codex-rs/tui/src/app/platform_actions.rs index f19ed0bb60..7beb30c43a 100644 --- a/codex-rs/tui/src/app/platform_actions.rs +++ b/codex-rs/tui/src/app/platform_actions.rs @@ -21,10 +21,10 @@ impl App { permission_profile: PermissionProfile, tx: AppEventSender, ) { - let Ok(sandbox_policy) = permission_profile.to_legacy_sandbox_policy(cwd.as_path()) else { - send_world_writable_scan_failed(&tx); - return; - }; + let sandbox_policy = crate::permission_compat::legacy_compatible_sandbox_policy( + &permission_profile, + cwd.as_path(), + ); tokio::task::spawn_blocking(move || { let logs_base_dir_path = logs_base_dir.as_path(); diff --git a/codex-rs/tui/src/app_command.rs b/codex-rs/tui/src/app_command.rs index d60da69d1d..aa40321903 100644 --- a/codex-rs/tui/src/app_command.rs +++ b/codex-rs/tui/src/app_command.rs @@ -24,7 +24,7 @@ use codex_protocol::user_input::UserInput; use serde::Serialize; use serde_json::Value; -use crate::permission_compat::legacy_compatible_permission_profile; +use crate::permission_compat::legacy_compatible_sandbox_policy; #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, PartialEq, Serialize)] @@ -371,15 +371,8 @@ impl AppCommand { collaboration_mode, personality, } => { - let legacy_profile = - legacy_compatible_permission_profile(&permission_profile, cwd.as_path()); - let sandbox_policy = legacy_profile - .to_legacy_sandbox_policy(cwd.as_path()) - .unwrap_or_else(|err| { - unreachable!( - "legacy-compatible permissions must project to legacy policy: {err}" - ) - }); + let sandbox_policy = + legacy_compatible_sandbox_policy(&permission_profile, cwd.as_path()); Op::UserTurn { items, environments: None, @@ -613,9 +606,8 @@ impl From for AppCommand { personality, } => { if environments.is_none() - && legacy_compatible_permission_profile(&permission_profile, cwd.as_path()) - .to_legacy_sandbox_policy(cwd.as_path()) - .is_ok_and(|compatible_policy| compatible_policy == sandbox_policy) + && legacy_compatible_sandbox_policy(&permission_profile, cwd.as_path()) + == sandbox_policy { Self::UserTurn { items, diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index c57875f1a3..0fa30f5248 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -3,7 +3,7 @@ use crate::bottom_pane::FeedbackAudience; use crate::legacy_core::append_message_history_entry; use crate::legacy_core::config::Config; use crate::legacy_core::message_history_metadata; -use crate::permission_compat::legacy_compatible_permission_profile; +use crate::permission_compat::legacy_compatible_sandbox_policy; use crate::status::StatusAccountDisplay; use crate::status::plan_type_display_name; use codex_app_server_client::AppServerClient; @@ -1159,15 +1159,7 @@ fn turn_permissions_overrides( }; let sandbox_policy = (matches!(thread_params_mode, ThreadParamsMode::Remote) || permissions.is_none()) - .then(|| { - let legacy_profile = legacy_compatible_permission_profile(permission_profile, cwd); - let policy = legacy_profile - .to_legacy_sandbox_policy(cwd) - .unwrap_or_else(|err| { - unreachable!("legacy-compatible permissions must project to legacy policy: {err}") - }); - policy.into() - }); + .then(|| legacy_compatible_sandbox_policy(permission_profile, cwd).into()); (sandbox_policy, permissions) } diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index cdd31e5329..85024644af 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -9682,14 +9682,11 @@ impl ChatWidget { } let cwd = self.config.cwd.clone(); let env_map: std::collections::HashMap = std::env::vars().collect(); - let Ok(policy) = self - .config - .permissions - .permission_profile() - .to_legacy_sandbox_policy(self.config.cwd.as_path()) - else { - return Some((Vec::new(), 0, true)); - }; + let permission_profile = self.config.permissions.permission_profile(); + let policy = crate::permission_compat::legacy_compatible_sandbox_policy( + &permission_profile, + self.config.cwd.as_path(), + ); match codex_windows_sandbox::apply_world_writable_scan_and_denies( self.config.codex_home.as_path(), cwd.as_path(), diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 39b4294b4a..93446a53e5 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -2210,9 +2210,10 @@ mod tests { .clone() .unwrap_or_else(|| "gpt-5.1".to_string()); let permission_profile = config.permissions.permission_profile(); - let sandbox_policy = permission_profile - .to_legacy_sandbox_policy(config.cwd.as_path()) - .expect("configured permissions must have a legacy compatibility projection"); + let sandbox_policy = permission_compat::legacy_compatible_sandbox_policy( + &permission_profile, + config.cwd.as_path(), + ); TurnContextItem { turn_id: None, trace_id: None, diff --git a/codex-rs/tui/src/permission_compat.rs b/codex-rs/tui/src/permission_compat.rs index 97e4436ef6..669b9fef05 100644 --- a/codex-rs/tui/src/permission_compat.rs +++ b/codex-rs/tui/src/permission_compat.rs @@ -4,9 +4,21 @@ use codex_protocol::models::PermissionProfile; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; +use codex_protocol::protocol::SandboxPolicy; use codex_utils_absolute_path::AbsolutePathBuf; use std::path::Path; +pub(crate) fn legacy_compatible_sandbox_policy( + permission_profile: &PermissionProfile, + cwd: &Path, +) -> SandboxPolicy { + legacy_compatible_permission_profile(permission_profile, cwd) + .to_legacy_sandbox_policy(cwd) + .unwrap_or_else(|err| { + unreachable!("legacy-compatible permissions must project to legacy policy: {err}") + }) +} + pub(crate) fn legacy_compatible_permission_profile( permission_profile: &PermissionProfile, cwd: &Path,