diff --git a/codex-rs/core/src/windows_sandbox.rs b/codex-rs/core/src/windows_sandbox.rs index a1ff5dda09..d0fe013c7a 100644 --- a/codex-rs/core/src/windows_sandbox.rs +++ b/codex-rs/core/src/windows_sandbox.rs @@ -169,6 +169,41 @@ pub fn run_elevated_setup( ) } +#[cfg(target_os = "windows")] +pub fn elevated_setup_is_ready( + permission_profile: &PermissionProfile, + workspace_roots: &[AbsolutePathBuf], + command_cwd: &Path, + env_map: &HashMap, + codex_home: &Path, +) -> bool { + if !sandbox_setup_is_complete(codex_home) { + return false; + } + + codex_windows_sandbox::run_setup_refresh_with_extra_read_roots( + permission_profile, + workspace_roots, + command_cwd, + env_map, + codex_home, + Vec::new(), + /*proxy_enforced*/ false, + ) + .is_ok() +} + +#[cfg(not(target_os = "windows"))] +pub fn elevated_setup_is_ready( + _permission_profile: &PermissionProfile, + _workspace_roots: &[AbsolutePathBuf], + _command_cwd: &Path, + _env_map: &HashMap, + _codex_home: &Path, +) -> bool { + false +} + #[cfg(target_os = "windows")] pub fn run_elevated_provisioning_setup(codex_home: &Path, real_user: &str) -> anyhow::Result<()> { codex_windows_sandbox::run_elevated_provisioning_setup(codex_home, real_user) @@ -307,7 +342,13 @@ async fn run_windows_sandbox_setup_and_persist( let setup_result = tokio::task::spawn_blocking(move || -> anyhow::Result<()> { match mode { WindowsSandboxSetupMode::Elevated => { - if !sandbox_setup_is_complete(setup_codex_home.as_path()) { + if !elevated_setup_is_ready( + &permission_profile, + workspace_roots.as_slice(), + command_cwd.as_path(), + &env_map, + setup_codex_home.as_path(), + ) { run_elevated_setup( &permission_profile, workspace_roots.as_slice(), diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 16e4aaa627..2777f7790c 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -1029,9 +1029,15 @@ impl App { let codex_home = self.config.codex_home.clone(); let tx = self.app_event_tx.clone(); - // If the elevated setup already ran on this machine, don't prompt for - // elevation again - just flip the config to use the elevated path. - if crate::windows_sandbox::sandbox_setup_is_complete(codex_home.as_path()) { + // Skip the UAC path only when the existing setup still refreshes cleanly + // for the current workspace roots and permission profile. + if crate::windows_sandbox::elevated_setup_is_ready( + &permission_profile, + workspace_roots.as_slice(), + command_cwd.as_path(), + &env_map, + codex_home.as_path(), + ) { tx.send(AppEvent::EnableWindowsSandboxForAgentMode { preset, mode: WindowsSandboxEnableMode::Elevated, diff --git a/codex-rs/tui/src/chatwidget/permission_popups.rs b/codex-rs/tui/src/chatwidget/permission_popups.rs index 1283a9e490..1ed4532680 100644 --- a/codex-rs/tui/src/chatwidget/permission_popups.rs +++ b/codex-rs/tui/src/chatwidget/permission_popups.rs @@ -328,7 +328,13 @@ impl ChatWidget { == WindowsSandboxLevel::Disabled { let preset = preset.clone(); - if crate::windows_sandbox::sandbox_setup_is_complete( + let env_map: std::collections::HashMap = + std::env::vars().collect(); + if crate::windows_sandbox::elevated_setup_is_ready( + &self.config.permissions.effective_permission_profile(), + self.config.effective_workspace_roots().as_slice(), + self.config.cwd.as_path(), + &env_map, self.config.codex_home.as_path(), ) { return vec![Box::new(move |tx| { diff --git a/codex-rs/tui/src/windows_sandbox.rs b/codex-rs/tui/src/windows_sandbox.rs index ec586e7b1b..b38f2d670b 100644 --- a/codex-rs/tui/src/windows_sandbox.rs +++ b/codex-rs/tui/src/windows_sandbox.rs @@ -42,6 +42,41 @@ pub(crate) fn sandbox_setup_is_complete(_codex_home: &Path) -> bool { false } +#[cfg(target_os = "windows")] +pub(crate) fn elevated_setup_is_ready( + permission_profile: &PermissionProfile, + workspace_roots: &[AbsolutePathBuf], + command_cwd: &Path, + env_map: &HashMap, + codex_home: &Path, +) -> bool { + if !sandbox_setup_is_complete(codex_home) { + return false; + } + + codex_windows_sandbox::run_setup_refresh_with_extra_read_roots( + permission_profile, + workspace_roots, + command_cwd, + env_map, + codex_home, + Vec::new(), + /*proxy_enforced*/ false, + ) + .is_ok() +} + +#[cfg(not(target_os = "windows"))] +pub(crate) fn elevated_setup_is_ready( + _permission_profile: &PermissionProfile, + _workspace_roots: &[AbsolutePathBuf], + _command_cwd: &Path, + _env_map: &HashMap, + _codex_home: &Path, +) -> bool { + false +} + #[cfg(target_os = "windows")] pub(crate) fn run_elevated_setup( permission_profile: &PermissionProfile,