Files
codex/codex-rs/tui/src/resume_permissions.rs
Eric Trautandcopyberry 0d7b8117d3 Support projectless TUI sessions with workspace defaults (#49160)
## What changed

- Skip folder trust prompts for locally discovered projectless directories without a saved trust decision. Apply workspace-write permissions and granular approval defaults only when local execution, configuration, and managed policy allow them.
- Prompt for Windows sandbox setup when needed before enabling implicit workspace-write permissions.
- Restore saved task permissions on resume unless explicitly overridden, and keep ordinary turns from overwriting the server's saved permission profile. Track explicit approval reviewer choices separately from restored permissions.
- Let `/cd` request folder trust with a “Keep current directory” option. Recheck task activity and background terminals before switching, and enforce destination permission requirements.

## Testing

Add regression coverage for projectless defaults and session transitions, explicit permission overrides, saved profile preservation, trust discovery, destination policy conflicts, and the directory consent and Windows sandbox prompts.

GitOrigin-RevId: cf74f5a90245f2aee96caa80d76de64da6ec1ebd
2026-09-29 04:13:14 +00:00

58 lines
2.1 KiB
Rust

//! Tracks direct launch permission choices separately from configuration defaults.
//! Omitted choices let app-server restore the destination task's saved settings.
use crate::legacy_core::config::Config;
use crate::legacy_core::config::ConfigOverrides;
use codex_config::ConfigLayerSource;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct ResumePermissions {
pub approval_policy: bool,
pub approvals_reviewer: bool,
pub profile: bool,
pub workspace_roots: bool,
}
impl ResumePermissions {
pub(crate) fn from_overrides(config: &Config, overrides: &ConfigOverrides) -> Self {
let has = |path: &str| {
config.config_layer_stack.layers_high_to_low().any(|layer| {
matches!(layer.name, ConfigLayerSource::SessionFlags)
&& path
.split('.')
.try_fold(&layer.config, |value, key| value.get(key))
.is_some()
})
};
Self {
approval_policy: overrides.approval_policy.is_some() || has("approval_policy"),
approvals_reviewer: overrides.approvals_reviewer.is_some() || has("approvals_reviewer"),
profile: overrides.sandbox_mode.is_some()
|| overrides.permission_profile.is_some()
|| overrides.default_permissions.is_some()
|| !overrides.additional_writable_roots.is_empty()
|| has("default_permissions")
|| has("sandbox_mode")
|| has("sandbox_workspace_write")
|| has("permissions")
|| has("network"),
workspace_roots: overrides.cwd.is_some()
|| overrides.workspace_roots.is_some()
|| !overrides.additional_writable_roots.is_empty()
|| has("sandbox_workspace_write.writable_roots"),
}
}
#[cfg(test)]
pub(crate) const CURRENT_CONFIG: Self = Self {
approval_policy: true,
approvals_reviewer: true,
profile: true,
workspace_roots: true,
};
}
#[cfg(test)]
#[path = "resume_permissions_tests.rs"]
mod tests;