Handle "Don't Trust" directory selection in onboarding (#4941)

Fixes #4940
Fixes #4892

When selecting "No, ask me to approve edits and commands" during
onboarding, the code wasn't applying the correct approval policy,
causing Codex to block all write operations instead of requesting
approval.

This PR fixes the issue by persisting the "DontTrust" decision in
config.toml as `trust_level = "untrusted"` and handling it in the
sandbox and approval policy logic, so Codex correctly asks for approval
before making changes.

## Before (bug)
<img width="709" height="500" alt="bef"
src="https://github.com/user-attachments/assets/5aced26d-d810-4754-879a-89d9e4e0073b"
/>

## After (fixed)
<img width="713" height="359" alt="aft"
src="https://github.com/user-attachments/assets/9887bbcb-a9a5-4e54-8e76-9125a782226b"
/>

---------

Co-authored-by: Eric Traut <etraut@openai.com>
This commit is contained in:
Vinicius da Motta
2025-11-14 15:23:35 -08:00
committed by GitHub
co-authored by Eric Traut
parent 018a2d2e50
commit 89ecc00b79
5 changed files with 173 additions and 35 deletions
+25 -10
View File
@@ -82,7 +82,6 @@ mod wrapping;
#[cfg(test)]
pub mod test_backend;
use crate::onboarding::TrustDirectorySelection;
use crate::onboarding::WSL_INSTRUCTIONS;
use crate::onboarding::onboarding_screen::OnboardingScreenArgs;
use crate::onboarding::onboarding_screen::run_onboarding_app;
@@ -378,13 +377,8 @@ async fn run_ratatui_app(
update_action: None,
});
}
// if the user acknowledged windows or made an explicit decision ato trust the directory, reload the config accordingly
if should_show_windows_wsl_screen
|| onboarding_result
.directory_trust_decision
.map(|d| d == TrustDirectorySelection::Trust)
.unwrap_or(false)
{
// if the user acknowledged windows or made any trust decision, reload the config accordingly
if should_show_windows_wsl_screen || onboarding_result.directory_trust_decision.is_some() {
load_config_or_exit(cli_kv_overrides, overrides).await
} else {
initial_config
@@ -540,8 +534,8 @@ fn should_show_trust_screen(config: &Config) -> bool {
// Respect explicit approval/sandbox overrides made by the user.
return false;
}
// otherwise, skip iff the active project is trusted
!config.active_project.is_trusted()
// otherwise, show only if no trust decision has been made
config.active_project.trust_level.is_none()
}
fn should_show_onboarding(
@@ -635,4 +629,25 @@ mod tests {
}
Ok(())
}
#[test]
fn untrusted_project_skips_trust_prompt() -> std::io::Result<()> {
use codex_protocol::config_types::TrustLevel;
let temp_dir = TempDir::new()?;
let mut config = Config::load_from_base_config_with_overrides(
ConfigToml::default(),
ConfigOverrides::default(),
temp_dir.path().to_path_buf(),
)?;
config.did_user_set_custom_approval_policy_or_sandbox_mode = false;
config.active_project = ProjectConfig {
trust_level: Some(TrustLevel::Untrusted),
};
let should_show = should_show_trust_screen(&config);
assert!(
!should_show,
"Trust prompt should not be shown for projects explicitly marked as untrusted"
);
Ok(())
}
}
+13 -2
View File
@@ -1,7 +1,8 @@
use std::path::PathBuf;
use codex_core::config::set_project_trusted;
use codex_core::config::set_project_trust_level;
use codex_core::git_info::resolve_root_git_project_for_trust;
use codex_protocol::config_types::TrustLevel;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyEventKind;
@@ -153,7 +154,7 @@ impl TrustDirectoryWidget {
fn handle_trust(&mut self) {
let target =
resolve_root_git_project_for_trust(&self.cwd).unwrap_or_else(|| self.cwd.clone());
if let Err(e) = set_project_trusted(&self.codex_home, &target) {
if let Err(e) = set_project_trust_level(&self.codex_home, &target, TrustLevel::Trusted) {
tracing::error!("Failed to set project trusted: {e:?}");
self.error = Some(format!("Failed to set trust for {}: {e}", target.display()));
}
@@ -163,6 +164,16 @@ impl TrustDirectoryWidget {
fn handle_dont_trust(&mut self) {
self.highlighted = TrustDirectorySelection::DontTrust;
let target =
resolve_root_git_project_for_trust(&self.cwd).unwrap_or_else(|| self.cwd.clone());
if let Err(e) = set_project_trust_level(&self.codex_home, &target, TrustLevel::Untrusted) {
tracing::error!("Failed to set project untrusted: {e:?}");
self.error = Some(format!(
"Failed to set untrusted for {}: {e}",
target.display()
));
}
self.selection = Some(TrustDirectorySelection::DontTrust);
}
}