mirror of
https://github.com/openai/codex.git
synced 2026-10-21 05:22:21 +00:00
Use app-server configuration for Windows sandbox state in the TUI (#45830)
## Why The TUI's Windows sandbox turn-context override was ignored by `thread/settings/update`, and onboarding derived sandbox state from local configuration instead of the app server's effective configuration. ## What changed - Read Windows sandbox configuration from the app server before showing the onboarding sandbox creation hint. Show the hint only when the read succeeds and the sandbox is disabled. - Remove `windows_sandbox_level` from `AppCommand::OverrideTurnContext` and stop sending sandbox-only overrides after setup or feature changes. - Rely on the effective configuration refresh after setup instead of updating local sandbox flags manually. ## Testing Update the trust-directory rendering test and snapshot to cover the sandbox creation hint. GitOrigin-RevId: 2ee6b0634812e7b39fd73dab3ae344729910ba70
This commit is contained in:
@@ -228,7 +228,6 @@ impl App {
|
||||
approvals_reviewer,
|
||||
Some(permission_profile.clone()),
|
||||
active_permission_profile,
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
@@ -588,12 +587,6 @@ impl App {
|
||||
|
||||
let auto_review_preset = auto_review_mode();
|
||||
let mut next_config = self.config.clone();
|
||||
let windows_sandbox_changed = updates.iter().any(|(feature, _)| {
|
||||
matches!(
|
||||
feature,
|
||||
Feature::WindowsSandbox | Feature::WindowsSandboxElevated
|
||||
)
|
||||
});
|
||||
let mut approval_policy_override = None;
|
||||
let mut approvals_reviewer_override = None;
|
||||
let mut permission_profile_override = None;
|
||||
@@ -733,9 +726,6 @@ impl App {
|
||||
&feature_updates_to_apply,
|
||||
)
|
||||
.await;
|
||||
if windows_sandbox_changed {
|
||||
self.propagate_windows_sandbox_turn_context();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -803,7 +793,6 @@ impl App {
|
||||
approvals_reviewer_override,
|
||||
permission_profile_override,
|
||||
active_permission_profile_override,
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
@@ -820,10 +809,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
if windows_sandbox_changed {
|
||||
self.propagate_windows_sandbox_turn_context();
|
||||
}
|
||||
|
||||
if let Some(label) = permissions_history_label {
|
||||
self.chat_widget.add_info_message(
|
||||
format!("Permissions updated to {label}"),
|
||||
@@ -1205,7 +1190,6 @@ impl App {
|
||||
Some(self.config.approvals_reviewer),
|
||||
/*permission_profile*/ None,
|
||||
Some(auto_review_preset.active_permission_profile),
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
@@ -1265,28 +1249,6 @@ impl App {
|
||||
self.chat_widget.add_error_message("Windows sandbox setup completed, but its mode was overridden by the effective configuration.".to_string());
|
||||
false
|
||||
}
|
||||
|
||||
fn propagate_windows_sandbox_turn_context(&self) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config);
|
||||
self.app_event_tx
|
||||
.send(AppEvent::CodexOp(AppCommand::override_turn_context(
|
||||
/*cwd*/ None,
|
||||
/*approval_policy*/ None,
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
Some(windows_sandbox_level),
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
/*service_tier*/ None,
|
||||
/*collaboration_mode*/ None,
|
||||
/*personality*/ None,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn overridden_write_message(write_response: &ConfigWriteResponse) -> &str {
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::pager_overlay::TranscriptHistoryState;
|
||||
use crate::session_resume::cwds_differ;
|
||||
use codex_app_server_protocol::ThreadGoalStatus;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_app_server_protocol::WindowsSandboxSetupMode as WindowsSandboxModeToml;
|
||||
use codex_app_server_protocol::WindowsSandboxSetupMode;
|
||||
|
||||
pub(super) const SHUTDOWN_FIRST_EXIT_TIMEOUT: Duration = Duration::from_secs(/*secs*/ 2);
|
||||
|
||||
@@ -838,15 +838,13 @@ impl App {
|
||||
approvals_reviewer,
|
||||
permission_profile,
|
||||
active_permission_profile,
|
||||
windows_sandbox_level,
|
||||
..
|
||||
} = &op
|
||||
&& (cwd.is_some()
|
||||
|| approval_policy.is_some()
|
||||
|| approvals_reviewer.is_some()
|
||||
|| permission_profile.is_some()
|
||||
|| active_permission_profile.is_some()
|
||||
|| windows_sandbox_level.is_some())
|
||||
|| active_permission_profile.is_some())
|
||||
&& self.reject_pending_permission_change()
|
||||
{
|
||||
return Ok(AppRunControl::Continue);
|
||||
@@ -2043,46 +2041,17 @@ impl App {
|
||||
);
|
||||
}
|
||||
let selected_mode = match mode {
|
||||
WindowsSandboxEnableMode::Elevated => WindowsSandboxModeToml::Elevated,
|
||||
WindowsSandboxEnableMode::Legacy => WindowsSandboxModeToml::Unelevated,
|
||||
WindowsSandboxEnableMode::Elevated => WindowsSandboxSetupMode::Elevated,
|
||||
WindowsSandboxEnableMode::Legacy => WindowsSandboxSetupMode::Unelevated,
|
||||
};
|
||||
let elevated_enabled = selected_mode == WindowsSandboxModeToml::Elevated;
|
||||
let elevated_enabled = selected_mode == WindowsSandboxSetupMode::Elevated;
|
||||
if self
|
||||
.verify_windows_sandbox_mode_after_setup(app_server, selected_mode)
|
||||
.await
|
||||
{
|
||||
self.chat_widget.windows_sandbox_elevated_setup_complete =
|
||||
elevated_enabled;
|
||||
if elevated_enabled {
|
||||
self.config.set_windows_sandbox_enabled(/*value*/ false);
|
||||
self.config
|
||||
.set_windows_elevated_sandbox_enabled(/*value*/ true);
|
||||
} else {
|
||||
self.config.set_windows_sandbox_enabled(/*value*/ true);
|
||||
self.config
|
||||
.set_windows_elevated_sandbox_enabled(/*value*/ false);
|
||||
}
|
||||
self.chat_widget.set_windows_sandbox_mode(Some(selected_mode));
|
||||
let windows_sandbox_level =
|
||||
crate::windows_sandbox::level_from_config(&self.config);
|
||||
if let Some(selection) = profile_selection {
|
||||
self.app_event_tx.send(AppEvent::CodexOp(
|
||||
AppCommand::override_turn_context(
|
||||
/*cwd*/ None,
|
||||
/*approval_policy*/ None,
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
#[cfg(target_os = "windows")]
|
||||
Some(windows_sandbox_level),
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
/*service_tier*/ None,
|
||||
/*collaboration_mode*/ None,
|
||||
/*personality*/ None,
|
||||
),
|
||||
));
|
||||
if self.apply_permission_profile_selection(selection).await {
|
||||
self.chat_widget.submit_initial_user_message_if_pending();
|
||||
}
|
||||
@@ -2094,8 +2063,6 @@ impl App {
|
||||
Some(self.config.approvals_reviewer),
|
||||
Some(preset.permission_profile.clone()),
|
||||
Some(preset.active_permission_profile.clone()),
|
||||
#[cfg(target_os = "windows")]
|
||||
Some(windows_sandbox_level),
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::*;
|
||||
#[cfg(all(test, not(target_os = "windows")))]
|
||||
use crate::app_event::WindowsSandboxEnableMode;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_app_server_protocol::WindowsSandboxSetupMode as WindowsSandboxModeToml;
|
||||
use codex_app_server_protocol::WindowsSandboxSetupMode;
|
||||
#[cfg(any(target_os = "windows", test))]
|
||||
use codex_utils_approval_presets::ApprovalPreset;
|
||||
|
||||
@@ -158,15 +158,9 @@ impl App {
|
||||
profile_selection: Option<PermissionProfileSelection>,
|
||||
mode: WindowsSandboxEnableMode,
|
||||
) {
|
||||
let (setup_mode, config_mode) = match mode {
|
||||
WindowsSandboxEnableMode::Elevated => (
|
||||
codex_app_server_protocol::WindowsSandboxSetupMode::Elevated,
|
||||
WindowsSandboxModeToml::Elevated,
|
||||
),
|
||||
WindowsSandboxEnableMode::Legacy => (
|
||||
codex_app_server_protocol::WindowsSandboxSetupMode::Unelevated,
|
||||
WindowsSandboxModeToml::Unelevated,
|
||||
),
|
||||
let setup_mode = match mode {
|
||||
WindowsSandboxEnableMode::Elevated => WindowsSandboxSetupMode::Elevated,
|
||||
WindowsSandboxEnableMode::Legacy => WindowsSandboxSetupMode::Unelevated,
|
||||
};
|
||||
if self.windows_sandbox.pending_setup.is_some() {
|
||||
if self.windows_sandbox.setup_started_at.is_none() {
|
||||
@@ -180,7 +174,7 @@ impl App {
|
||||
if !self.refresh_windows_sandbox_config(app_server).await {
|
||||
return;
|
||||
}
|
||||
if !self.chat_widget.windows_sandbox_config.allows(config_mode) {
|
||||
if !self.chat_widget.windows_sandbox_config.allows(setup_mode) {
|
||||
self.chat_widget
|
||||
.retain_input_after_failed_permission_selection();
|
||||
self.chat_widget.add_info_message(
|
||||
|
||||
@@ -3239,7 +3239,6 @@ default_permissions = "locked-down"
|
||||
approvals_reviewer: None,
|
||||
permission_profile: Some(app.config.permissions.permission_profile().clone()),
|
||||
active_permission_profile: app.config.permissions.active_permission_profile(),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
@@ -3333,7 +3332,6 @@ async fn update_feature_flags_enabling_guardian_selects_auto_review() -> Result<
|
||||
approvals_reviewer: Some(auto_review.approvals_reviewer),
|
||||
permission_profile: Some(auto_review.permission_profile()),
|
||||
active_permission_profile: Some(auto_review.active_permission_profile.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
@@ -3428,7 +3426,6 @@ async fn update_feature_flags_disabling_guardian_clears_review_policy_and_restor
|
||||
approvals_reviewer: Some(ApprovalsReviewer::User),
|
||||
permission_profile: None,
|
||||
active_permission_profile: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
@@ -3509,7 +3506,6 @@ async fn update_feature_flags_enabling_guardian_overrides_explicit_manual_review
|
||||
approvals_reviewer: Some(auto_review.approvals_reviewer),
|
||||
permission_profile: Some(auto_review.permission_profile()),
|
||||
active_permission_profile: Some(auto_review.active_permission_profile.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
@@ -3569,7 +3565,6 @@ async fn update_feature_flags_disabling_guardian_clears_manual_review_policy_wit
|
||||
approvals_reviewer: Some(ApprovalsReviewer::User),
|
||||
permission_profile: None,
|
||||
active_permission_profile: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
@@ -8713,7 +8708,6 @@ async fn override_turn_context_sends_thread_settings_update() {
|
||||
Some(ActivePermissionProfile::new(
|
||||
codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE,
|
||||
)),
|
||||
/*windows_sandbox_level*/ None,
|
||||
Some("gpt-5.4".to_string()),
|
||||
Some(Some(ReasoningEffortConfig::High)),
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -157,9 +157,6 @@ impl App {
|
||||
approvals_reviewer,
|
||||
permission_profile: _,
|
||||
active_permission_profile,
|
||||
// TODO(anp): Support Windows sandbox updates through environment configuration;
|
||||
// thread/settings/update cannot currently represent this override.
|
||||
windows_sandbox_level: _,
|
||||
model,
|
||||
effort,
|
||||
summary,
|
||||
|
||||
@@ -16,7 +16,6 @@ use codex_protocol::approvals::GuardianAssessmentEvent;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::ActivePermissionProfile;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -138,7 +137,6 @@ pub(crate) enum AppCommand {
|
||||
approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
permission_profile: Option<PermissionProfile>,
|
||||
active_permission_profile: Option<ActivePermissionProfile>,
|
||||
windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
model: Option<String>,
|
||||
effort: Option<Option<ReasoningEffortConfig>>,
|
||||
summary: Option<ReasoningSummaryConfig>,
|
||||
@@ -255,7 +253,6 @@ impl AppCommand {
|
||||
approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
permission_profile: Option<PermissionProfile>,
|
||||
active_permission_profile: Option<ActivePermissionProfile>,
|
||||
windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
model: Option<String>,
|
||||
effort: Option<Option<ReasoningEffortConfig>>,
|
||||
summary: Option<ReasoningSummaryConfig>,
|
||||
@@ -269,7 +266,6 @@ impl AppCommand {
|
||||
approvals_reviewer,
|
||||
permission_profile,
|
||||
active_permission_profile,
|
||||
windows_sandbox_level,
|
||||
model,
|
||||
effort,
|
||||
summary,
|
||||
|
||||
@@ -274,7 +274,6 @@ impl ChatWidget {
|
||||
Some(approvals_reviewer),
|
||||
Some(permission_profile.clone()),
|
||||
Some(active_permission_profile.clone()),
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -450,7 +450,6 @@ impl ChatWidget {
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
/*windows_sandbox_level*/ None,
|
||||
Some(switch_model_for_events.clone()),
|
||||
Some(Some(default_effort.clone())),
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -113,7 +113,6 @@ impl ChatWidget {
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -697,7 +697,6 @@ impl ChatWidget {
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
|
||||
@@ -1351,7 +1351,6 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context
|
||||
active_permission_profile: Some(ActivePermissionProfile::new(
|
||||
BUILT_IN_PERMISSION_PROFILE_WORKSPACE,
|
||||
)),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
||||
@@ -139,6 +139,22 @@ impl OnboardingScreen {
|
||||
tui.frame_requester(),
|
||||
local_settings.tui.animations,
|
||||
)));
|
||||
#[cfg(target_os = "windows")]
|
||||
let show_windows_create_sandbox_hint = if show_trust_screen
|
||||
&& remote_project_trust.is_none()
|
||||
&& let Some(handle) = &app_server_request_handle
|
||||
{
|
||||
crate::windows_sandbox::WindowsSandboxConfig::read(
|
||||
handle.clone(),
|
||||
config.cwd.display().to_string(),
|
||||
)
|
||||
.await
|
||||
.is_ok_and(|state| state.level() == WindowsSandboxLevel::Disabled)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let show_windows_create_sandbox_hint = false;
|
||||
if show_login_screen {
|
||||
let highlighted_mode =
|
||||
if auth_config.is_login_method_allowed(ForcedLoginMethod::Chatgpt) {
|
||||
@@ -163,11 +179,6 @@ impl OnboardingScreen {
|
||||
tracing::warn!("skipping onboarding login step without app-server request handle");
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
let show_windows_create_sandbox_hint = remote_project_trust.is_none()
|
||||
&& crate::windows_sandbox::level_from_config(&config) == WindowsSandboxLevel::Disabled;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let show_windows_create_sandbox_hint = false;
|
||||
let highlighted = TrustDirectorySelection::Trust;
|
||||
if show_trust_screen {
|
||||
let (cwd, trust_target) = match remote_project_trust {
|
||||
|
||||
+1
-1
@@ -13,4 +13,4 @@ expression: terminal.backend()
|
||||
› 1. Trust and continue
|
||||
2. Quit
|
||||
|
||||
Press enter to continue; esc to quit
|
||||
Press enter to continue and create a sandbox...
|
||||
|
||||
@@ -298,7 +298,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn renders_snapshot_for_git_repo() {
|
||||
let widget = widget(/*error*/ None);
|
||||
let mut widget = widget(/*error*/ None);
|
||||
widget.show_windows_create_sandbox_hint = true;
|
||||
|
||||
let mut terminal =
|
||||
Terminal::new(VT100Backend::new(/*width*/ 70, /*height*/ 14)).expect("terminal");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Windows sandbox configuration, managed requirements, and executor selection for the TUI.
|
||||
|
||||
use crate::legacy_core::config::Config;
|
||||
use codex_app_server_client::AppServerRequestHandle;
|
||||
use codex_app_server_protocol::ClientRequest;
|
||||
use codex_app_server_protocol::ConfigReadResponse;
|
||||
@@ -8,8 +7,6 @@ use codex_app_server_protocol::ConfigRequirementsReadResponse;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::WindowsSandboxImplementation;
|
||||
use codex_app_server_protocol::WindowsSandboxSetupMode;
|
||||
use codex_config::types::WindowsSandboxModeToml;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -131,21 +128,6 @@ impl WindowsSandboxConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
pub(crate) fn level_from_config(config: &Config) -> WindowsSandboxLevel {
|
||||
match config.permissions.windows_sandbox_mode {
|
||||
Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,
|
||||
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
|
||||
None if config.features.enabled(Feature::WindowsSandboxElevated) => {
|
||||
WindowsSandboxLevel::Elevated
|
||||
}
|
||||
None if config.features.enabled(Feature::WindowsSandbox) => {
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
}
|
||||
None => WindowsSandboxLevel::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
/// A server-local connection can still select remote executors. Missing selections are unknown.
|
||||
pub(crate) fn host_from_environments(
|
||||
environments: Option<&[codex_app_server_protocol::ThreadEnvironment]>,
|
||||
|
||||
Reference in New Issue
Block a user