From 108234b5ebe6941764a6b8edbb37b2aa04369f07 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Sat, 16 May 2026 07:26:18 -0700 Subject: [PATCH] core: set permission profiles from snapshots (#22920) ## Why #22891 moved the TUI turn-command path to pass `ActivePermissionProfile` instead of the full `PermissionProfile`, but the remaining config/session bridge still accepted the concrete `PermissionProfile` and active profile id as separate arguments. That shape made it too easy for future callers to update the concrete profile and active profile id out of sync. This PR makes the trusted session snapshot path pass one coherent value into `Permissions`, while keeping `requirements.toml` enforcement owned by the existing constrained permission state. ## What Changed - Added `PermissionProfileSnapshot` as the public snapshot value for trusted session/config synchronization. - Changed `Permissions::set_permission_profile_from_session_snapshot()` and `replace_permission_profile_from_session_snapshot()` to take a `PermissionProfileSnapshot`. - Updated the replacement path to derive its constrained `PermissionProfile` from the snapshot, so callers cannot pass a separate profile that disagrees with the snapshot. - Removed the internal tuple-style `PermissionProfileState::set_active_permission_profile()` mutation path. - Updated core session projection and TUI call sites to construct explicit legacy or active snapshots. - Documented the snapshot constructors so legacy use and id/profile mismatch hazards are called out at the API boundary. - Added a focused config test that verifies snapshot updates still respect existing permission constraints. ## How To Review 1. Start with `codex-rs/core/src/config/resolved_permission_profile.rs`; `PermissionProfileSnapshot` is the public wrapper, while `ResolvedPermissionProfile` stays internal. 2. Check `codex-rs/core/src/config/mod.rs` to confirm both session-snapshot setters validate through `PermissionProfileState` and no longer accept loose profile/id pairs. 3. Skim `codex-rs/core/src/session/session.rs` for the session projection path; it now builds the snapshot before installing it. 4. Skim the TUI changes as call-site migration from loose argument pairs to explicit snapshot construction. ## Verification - `cargo test -p codex-core permission_snapshot_setter_preserves_permission_constraints` - `cargo test -p codex-tui status_permissions_` - `cargo test -p codex-tui session_configured_preserves_profile_workspace_roots` - `just fix -p codex-core -p codex-tui` --- codex-rs/core/src/config/config_tests.rs | 24 ++++ codex-rs/core/src/config/mod.rs | 48 ++------ .../src/config/resolved_permission_profile.rs | 114 ++++++++++++++++-- codex-rs/core/src/session/mod.rs | 1 + codex-rs/core/src/session/session.rs | 19 ++- codex-rs/tui/src/app.rs | 1 + codex-rs/tui/src/app/config_persistence.rs | 12 +- codex-rs/tui/src/app/event_dispatch.rs | 6 +- codex-rs/tui/src/app/tests.rs | 11 +- codex-rs/tui/src/app/thread_session_state.rs | 7 +- codex-rs/tui/src/chatwidget.rs | 1 + codex-rs/tui/src/chatwidget/session_flow.rs | 14 +-- codex-rs/tui/src/chatwidget/settings.rs | 5 +- .../src/chatwidget/tests/history_replay.rs | 5 +- codex-rs/tui/src/status/tests.rs | 89 +++++++------- 15 files changed, 225 insertions(+), 132 deletions(-) diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 7a4a3a5c7..b8bd7d239 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -1453,6 +1453,30 @@ async fn permission_profile_override_populates_runtime_permissions() -> std::io: Ok(()) } +#[test] +fn permission_snapshot_setter_preserves_permission_constraints() { + let initial_profile = PermissionProfile::read_only(); + let mut permissions = Permissions::from_approval_and_profile( + Constrained::allow_any(AskForApproval::Never), + Constrained::allow_only(initial_profile.clone()), + ) + .expect("initial permissions should satisfy constraints"); + + let err = permissions + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( + PermissionProfile::workspace_write(), + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) + .expect_err("workspace profile should violate read-only constraint"); + + assert_eq!(permissions.permission_profile(), &initial_profile); + assert_eq!(permissions.active_permission_profile(), None); + assert!( + matches!(err, ConstraintError::InvalidValue { .. }), + "expected invalid value constraint error, got {err:?}" + ); +} + #[tokio::test] async fn permission_profile_override_preserves_managed_unrestricted_filesystem() -> std::io::Result<()> { diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index bad23da22..18158671e 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -149,6 +149,7 @@ pub use managed_features::ManagedFeatures; pub use network_proxy_spec::NetworkProxySpec; pub use network_proxy_spec::StartedNetworkProxy; pub(crate) use permissions::resolve_permission_profile; +pub use resolved_permission_profile::PermissionProfileSnapshot; pub(crate) use resolved_permission_profile::PermissionProfileState; const DEFAULT_IGNORE_LARGE_UNTRACKED_DIRS: i64 = 200; @@ -311,30 +312,13 @@ impl Permissions { /// /// This is a trusted-state bridge for consumers of `SessionConfigured`. /// Config loading and app-server selection should resolve named profiles - /// through config instead of constructing this pair directly. + /// through config instead of constructing a snapshot directly. pub fn set_permission_profile_from_session_snapshot( &mut self, - permission_profile: PermissionProfile, - active_permission_profile: Option, + snapshot: PermissionProfileSnapshot, ) -> ConstraintResult<()> { - self.set_permission_profile_from_session_snapshot_with_profile_workspace_roots( - permission_profile, - active_permission_profile, - Vec::new(), - ) - } - - pub fn set_permission_profile_from_session_snapshot_with_profile_workspace_roots( - &mut self, - permission_profile: PermissionProfile, - active_permission_profile: Option, - profile_workspace_roots: Vec, - ) -> ConstraintResult<()> { - self.permission_profile_state.set_active_permission_profile( - permission_profile, - active_permission_profile, - profile_workspace_roots, - ) + self.permission_profile_state + .set_permission_profile_snapshot(snapshot) } /// Replace the current permission constraints with a trusted session @@ -342,26 +326,12 @@ impl Permissions { /// after their local config constraints reject the snapshot. pub fn replace_permission_profile_from_session_snapshot( &mut self, - permission_profile: Constrained, - active_permission_profile: Option, + snapshot: PermissionProfileSnapshot, ) -> ConstraintResult<()> { - self.replace_permission_profile_from_session_snapshot_with_profile_workspace_roots( + let permission_profile = Constrained::allow_only(snapshot.permission_profile().clone()); + self.permission_profile_state = PermissionProfileState::from_constrained_resolved( permission_profile, - active_permission_profile, - Vec::new(), - ) - } - - pub fn replace_permission_profile_from_session_snapshot_with_profile_workspace_roots( - &mut self, - permission_profile: Constrained, - active_permission_profile: Option, - profile_workspace_roots: Vec, - ) -> ConstraintResult<()> { - self.permission_profile_state = PermissionProfileState::from_constrained_active_profile( - permission_profile, - active_permission_profile, - profile_workspace_roots, + snapshot.into_resolved_permission_profile(), )?; Ok(()) } diff --git a/codex-rs/core/src/config/resolved_permission_profile.rs b/codex-rs/core/src/config/resolved_permission_profile.rs index 911d9bf2e..f0fff66bc 100644 --- a/codex-rs/core/src/config/resolved_permission_profile.rs +++ b/codex-rs/core/src/config/resolved_permission_profile.rs @@ -40,6 +40,19 @@ pub(crate) enum ResolvedPermissionProfile { Named(NamedPermissionProfile), } +/// Trusted snapshot of a resolved permission profile. +/// +/// This is a bridge for already-resolved session/config state. It keeps the +/// concrete `PermissionProfile`, optional active profile id, and +/// profile-defined workspace roots together so `Permissions` can validate and +/// install them atomically. It is not a resolver: callers that are handling +/// user-selected profile ids should resolve those ids through config instead +/// of constructing this type directly. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PermissionProfileSnapshot { + resolved_permission_profile: ResolvedPermissionProfile, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct LegacyPermissionProfile { permission_profile: PermissionProfile, @@ -124,6 +137,93 @@ impl ResolvedPermissionProfile { } } +impl PermissionProfileSnapshot { + /// Create a snapshot with no active profile id. + /// + /// Prefer this only for legacy data or local overrides that genuinely do + /// not have a named/built-in profile identity. Using this for a built-in or + /// named profile will intentionally clear the active profile metadata. + pub fn legacy(permission_profile: PermissionProfile) -> Self { + Self { + resolved_permission_profile: ResolvedPermissionProfile::legacy(permission_profile), + } + } + + /// Create a snapshot for a known active profile id. + /// + /// Use this only after a trusted caller has already resolved the active id + /// to the supplied concrete `PermissionProfile`. This constructor does not + /// verify that the id and profile match; `Permissions` will still enforce + /// configured permission constraints when the snapshot is installed. + pub fn active( + permission_profile: PermissionProfile, + active_permission_profile: ActivePermissionProfile, + ) -> Self { + Self::active_with_profile_workspace_roots( + permission_profile, + active_permission_profile, + Vec::new(), + ) + } + + /// Create a snapshot for a known active profile id with profile roots. + /// + /// As with `active`, the caller is responsible for passing the concrete + /// profile and active id that were resolved together. Use this variant when + /// the selected profile declared workspace roots that should remain + /// distinct from turn-scoped runtime workspace roots. + pub fn active_with_profile_workspace_roots( + permission_profile: PermissionProfile, + active_permission_profile: ActivePermissionProfile, + profile_workspace_roots: Vec, + ) -> Self { + Self { + resolved_permission_profile: ResolvedPermissionProfile::from_active_profile( + permission_profile, + Some(active_permission_profile), + profile_workspace_roots, + ), + } + } + + /// Reconstruct a trusted snapshot from session state. + /// + /// This is intended for session responses emitted by core, where the + /// concrete profile and active profile id were captured together. Avoid + /// using this as a shortcut for arbitrary user input because mismatched + /// arguments can still misrepresent the active profile identity. + pub fn from_session_snapshot( + permission_profile: PermissionProfile, + active_permission_profile: Option, + ) -> Self { + match active_permission_profile { + Some(active_permission_profile) => { + Self::active(permission_profile, active_permission_profile) + } + None => Self::legacy(permission_profile), + } + } + + /// Borrow the concrete permission profile captured in this snapshot. + pub fn permission_profile(&self) -> &PermissionProfile { + self.resolved_permission_profile.permission_profile() + } + + /// Return the active profile id captured in this snapshot, if any. + pub fn active_permission_profile(&self) -> Option { + self.resolved_permission_profile.active_permission_profile() + } + + /// Borrow profile-declared workspace roots captured in this snapshot. + pub fn profile_workspace_roots(&self) -> &[AbsolutePathBuf] { + self.resolved_permission_profile.profile_workspace_roots() + } + + pub(crate) fn into_resolved_permission_profile(self) -> ResolvedPermissionProfile { + self.resolved_permission_profile + } +} + #[derive(Debug, Clone, PartialEq)] pub(crate) struct PermissionProfileState { resolved_permission_profile: Constrained, @@ -199,17 +299,11 @@ impl PermissionProfileState { .set(ResolvedPermissionProfile::legacy(permission_profile)) } - pub(crate) fn set_active_permission_profile( + pub(crate) fn set_permission_profile_snapshot( &mut self, - permission_profile: PermissionProfile, - active_permission_profile: Option, - profile_workspace_roots: Vec, + snapshot: PermissionProfileSnapshot, ) -> ConstraintResult<()> { - let candidate = ResolvedPermissionProfile::from_active_profile( - permission_profile, - active_permission_profile, - profile_workspace_roots, - ); - self.resolved_permission_profile.set(candidate) + self.resolved_permission_profile + .set(snapshot.into_resolved_permission_profile()) } } diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 2dd97710e..d67e79f79 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -173,6 +173,7 @@ use crate::compact::collect_user_messages; use crate::config::Config; use crate::config::Constrained; use crate::config::ConstraintResult; +use crate::config::PermissionProfileSnapshot; use crate::config::PermissionProfileState; use crate::config::StartedNetworkProxy; use crate::config::resolve_web_search_mode_for_turn; diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 5b57e1189..3f8276d04 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -357,11 +357,20 @@ impl SessionConfiguration { &file_system_sandbox_policy, network_sandbox_policy, ); - self.permission_profile_state.set_active_permission_profile( - effective_permission_profile, - active_permission_profile, - profile_workspace_roots, - ) + + let permission_snapshot = match active_permission_profile { + Some(active_permission_profile) => { + PermissionProfileSnapshot::active_with_profile_workspace_roots( + effective_permission_profile, + active_permission_profile, + profile_workspace_roots, + ) + } + None => PermissionProfileSnapshot::legacy(effective_permission_profile), + }; + + self.permission_profile_state + .set_permission_profile_snapshot(permission_snapshot) } } diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 00e45f979..46a7be00d 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -47,6 +47,7 @@ use crate::keymap::RuntimeKeymap; use crate::legacy_core::config::Config; use crate::legacy_core::config::ConfigBuilder; use crate::legacy_core::config::ConfigOverrides; +use crate::legacy_core::config::PermissionProfileSnapshot; use crate::legacy_core::config::edit::ConfigEdit; use crate::legacy_core::config::edit::ConfigEditsBuilder; #[cfg(target_os = "windows")] diff --git a/codex-rs/tui/src/app/config_persistence.rs b/codex-rs/tui/src/app/config_persistence.rs index 4a410f813..6f4fda608 100644 --- a/codex-rs/tui/src/app/config_persistence.rs +++ b/codex-rs/tui/src/app/config_persistence.rs @@ -128,10 +128,10 @@ impl App { if let Err(err) = config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( permission_profile.clone(), - Some(active_permission_profile), - ) + active_permission_profile, + )) { tracing::warn!(error = %err, "{log_message}"); self.chat_widget @@ -328,8 +328,10 @@ impl App { && let Err(err) = self .chat_widget .set_permission_profile_from_session_snapshot( - permission_profile.clone(), - active_permission_profile_override.clone(), + PermissionProfileSnapshot::from_session_snapshot( + permission_profile.clone(), + active_permission_profile_override.clone(), + ), ) { tracing::error!( diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 16e25e4d1..15e4f9b6c 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -1422,8 +1422,10 @@ impl App { if let Err(err) = self .chat_widget .set_permission_profile_from_session_snapshot( - permission_profile_for_chat, - Some(active_permission_profile), + PermissionProfileSnapshot::active( + permission_profile_for_chat, + active_permission_profile, + ), ) { tracing::warn!(%err, "failed to set permission profile on chat config"); diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index a925b4d48..c42f42c25 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -28,6 +28,7 @@ use crate::app_command::AppCommand as Op; use crate::diff_model::FileChange; use crate::legacy_core::config::ConfigBuilder; use crate::legacy_core::config::ConfigOverrides; +use crate::legacy_core::config::PermissionProfileSnapshot; use crate::legacy_core::config::TerminalResizeReflowMaxRows; use codex_app_server_protocol::AdditionalFileSystemPermissions; use codex_app_server_protocol::AdditionalNetworkPermissions; @@ -1730,10 +1731,9 @@ async fn update_feature_flags_disabling_guardian_clears_review_policy_and_restor app.chat_widget .set_approval_policy(AskForApproval::OnRequest); app.chat_widget - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::legacy( PermissionProfile::workspace_write(), - /*active_profile*/ None, - )?; + ))?; app.update_feature_flags(vec![(Feature::GuardianApproval, false)]) .await; @@ -3153,10 +3153,9 @@ async fn side_fork_config_inherits_parent_thread_runtime_settings() { app.chat_widget .set_approval_policy(AskForApproval::OnRequest); app.chat_widget - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::legacy( parent_permission_profile.clone(), - /*active_profile*/ None, - ) + )) .expect("test permission profile should be accepted"); app.chat_widget .set_approvals_reviewer(ApprovalsReviewer::AutoReview); diff --git a/codex-rs/tui/src/app/thread_session_state.rs b/codex-rs/tui/src/app/thread_session_state.rs index a531617e3..4037a085e 100644 --- a/codex-rs/tui/src/app/thread_session_state.rs +++ b/codex-rs/tui/src/app/thread_session_state.rs @@ -120,6 +120,7 @@ mod tests { use crate::app::side::SideThreadState; use crate::app::test_support::make_test_app; use crate::app::thread_events::ThreadEventChannel; + use crate::legacy_core::config::PermissionProfileSnapshot; use crate::test_support::PathBufExt; use crate::test_support::test_path_buf; use codex_app_server_protocol::AskForApproval; @@ -201,10 +202,10 @@ mod tests { ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE); app.chat_widget.handle_thread_session(main_session.clone()); app.chat_widget - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( expected_permission_profile.clone(), - Some(expected_active_permission_profile.clone()), - ) + expected_active_permission_profile.clone(), + )) .expect("set widget permission profile"); app.config .permissions diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 207c54756..a37371bf7 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -61,6 +61,7 @@ use crate::legacy_core::DEFAULT_AGENTS_MD_FILENAME; use crate::legacy_core::config::Config; use crate::legacy_core::config::Constrained; use crate::legacy_core::config::ConstraintResult; +use crate::legacy_core::config::PermissionProfileSnapshot; #[cfg(target_os = "windows")] use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt; use crate::mention_codec::LinkedMention; diff --git a/codex-rs/tui/src/chatwidget/session_flow.rs b/codex-rs/tui/src/chatwidget/session_flow.rs index 5dfb2090f..2b8beefdf 100644 --- a/codex-rs/tui/src/chatwidget/session_flow.rs +++ b/codex-rs/tui/src/chatwidget/session_flow.rs @@ -49,22 +49,20 @@ impl ChatWidget { self.config.permissions.approval_policy = Constrained::allow_only(session.approval_policy.to_core()); } + let permission_snapshot = PermissionProfileSnapshot::from_session_snapshot( + session.permission_profile.clone(), + session.active_permission_profile.clone(), + ); let permission_sync = self .config .permissions - .set_permission_profile_from_session_snapshot( - session.permission_profile.clone(), - session.active_permission_profile.clone(), - ); + .set_permission_profile_from_session_snapshot(permission_snapshot.clone()); if let Err(err) = permission_sync { tracing::warn!(%err, "failed to sync permissions from SessionConfigured"); if let Err(replace_err) = self .config .permissions - .replace_permission_profile_from_session_snapshot( - Constrained::allow_only(session.permission_profile.clone()), - session.active_permission_profile.clone(), - ) + .replace_permission_profile_from_session_snapshot(permission_snapshot) { tracing::error!( %replace_err, diff --git a/codex-rs/tui/src/chatwidget/settings.rs b/codex-rs/tui/src/chatwidget/settings.rs index a2125c3f4..91abc728d 100644 --- a/codex-rs/tui/src/chatwidget/settings.rs +++ b/codex-rs/tui/src/chatwidget/settings.rs @@ -20,12 +20,11 @@ impl ChatWidget { #[cfg_attr(not(target_os = "windows"), allow(dead_code))] pub(crate) fn set_permission_profile_from_session_snapshot( &mut self, - profile: PermissionProfile, - active_profile: Option, + snapshot: PermissionProfileSnapshot, ) -> ConstraintResult<()> { self.config .permissions - .set_permission_profile_from_session_snapshot(profile, active_profile)?; + .set_permission_profile_from_session_snapshot(snapshot)?; self.refresh_status_surfaces(); Ok(()) } diff --git a/codex-rs/tui/src/chatwidget/tests/history_replay.rs b/codex-rs/tui/src/chatwidget/tests/history_replay.rs index 24e408b01..36b3e0189 100644 --- a/codex-rs/tui/src/chatwidget/tests/history_replay.rs +++ b/codex-rs/tui/src/chatwidget/tests/history_replay.rs @@ -292,10 +292,9 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() { assert_eq!(&chat.config_ref().cwd, &expected_cwd); let updated_profile = PermissionProfile::workspace_write(); - chat.set_permission_profile_from_session_snapshot( + chat.set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::legacy( updated_profile.clone(), - /*active_profile*/ None, - ) + )) .expect("set permission profile"); assert_eq!( chat.config_ref().permissions.permission_profile(), diff --git a/codex-rs/tui/src/status/tests.rs b/codex-rs/tui/src/status/tests.rs index 019aebda7..3a10e0ed9 100644 --- a/codex-rs/tui/src/status/tests.rs +++ b/codex-rs/tui/src/status/tests.rs @@ -5,6 +5,7 @@ use super::rate_limit_snapshot_display; use crate::history_cell::HistoryCell; use crate::legacy_core::config::Config; use crate::legacy_core::config::ConfigBuilder; +use crate::legacy_core::config::PermissionProfileSnapshot; use crate::status::StatusAccountDisplay; use crate::test_support::PathBufExt; use crate::test_support::test_path_buf; @@ -304,10 +305,10 @@ async fn status_permissions_named_read_only_profile_shows_builtin_label() { .expect("set approval policy"); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::read_only(), - Some(ActivePermissionProfile::read_only()), - ) + ActivePermissionProfile::read_only(), + )) .expect("set permission profile"); assert_eq!( @@ -331,13 +332,13 @@ async fn status_permissions_read_only_profile_shows_additional_writable_roots() .with_additional_writable_roots(config.cwd.as_path(), std::slice::from_ref(&extra_root)); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::from_runtime_permissions( &file_system_policy, NetworkSandboxPolicy::Restricted, ), - Some(ActivePermissionProfile::read_only()), - ) + ActivePermissionProfile::read_only(), + )) .expect("set permission profile"); assert_eq!( @@ -357,12 +358,10 @@ async fn status_permissions_named_workspace_profile_shows_builtin_label() { .expect("set approval policy"); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write(), - Some(ActivePermissionProfile::new( - BUILT_IN_PERMISSION_PROFILE_WORKSPACE, - )), - ) + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) .expect("set permission profile"); assert_eq!( @@ -383,12 +382,10 @@ async fn status_permissions_workspace_auto_review_shows_reviewer_label() { .expect("set approval policy"); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write(), - Some(ActivePermissionProfile::new( - BUILT_IN_PERMISSION_PROFILE_WORKSPACE, - )), - ) + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) .expect("set permission profile"); assert_eq!( @@ -409,17 +406,15 @@ async fn status_permissions_named_profile_shows_additional_writable_roots() { let extra_root = test_path_buf("/workspace/extra").abs(); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write_with( std::slice::from_ref(&extra_root), NetworkSandboxPolicy::Restricted, /*exclude_tmpdir_env_var*/ false, /*exclude_slash_tmp*/ false, ), - Some(ActivePermissionProfile::new( - BUILT_IN_PERMISSION_PROFILE_WORKSPACE, - )), - ) + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) .expect("set permission profile"); assert_eq!( @@ -445,10 +440,10 @@ async fn status_permissions_workspace_roots_show_additional_directories() { .set_workspace_roots(config.workspace_roots.clone()); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write(), - Some(ActivePermissionProfile::new(":workspace")), - ) + ActivePermissionProfile::new(":workspace"), + )) .expect("set permission profile"); assert_eq!( @@ -470,15 +465,17 @@ async fn status_permissions_workspace_roots_include_profile_defined_directories( let profile_root = test_path_buf("/workspace/shared").abs(); config .permissions - .set_permission_profile_from_session_snapshot_with_profile_workspace_roots( - PermissionProfile::workspace_write_with( - std::slice::from_ref(&profile_root), - NetworkSandboxPolicy::Restricted, - /*exclude_tmpdir_env_var*/ false, - /*exclude_slash_tmp*/ false, + .set_permission_profile_from_session_snapshot( + PermissionProfileSnapshot::active_with_profile_workspace_roots( + PermissionProfile::workspace_write_with( + std::slice::from_ref(&profile_root), + NetworkSandboxPolicy::Restricted, + /*exclude_tmpdir_env_var*/ false, + /*exclude_slash_tmp*/ false, + ), + ActivePermissionProfile::new(":workspace"), + vec![profile_root.clone()], ), - Some(ActivePermissionProfile::new(":workspace")), - vec![profile_root.clone()], ) .expect("set permission profile"); @@ -502,17 +499,15 @@ async fn status_permissions_broadened_workspace_profile_shows_builtin_label() { .expect("set approval policy"); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write_with( &[], NetworkSandboxPolicy::Enabled, /*exclude_tmpdir_env_var*/ false, /*exclude_slash_tmp*/ false, ), - Some(ActivePermissionProfile::new( - BUILT_IN_PERMISSION_PROFILE_WORKSPACE, - )), - ) + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) .expect("set permission profile"); assert_eq!( @@ -527,10 +522,10 @@ async fn status_permissions_user_defined_profile_shows_name() { let mut config = test_config(&temp_home).await; config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::read_only(), - Some(ActivePermissionProfile::new("locked")), - ) + ActivePermissionProfile::new("locked"), + )) .expect("set permission profile"); assert_eq!( @@ -547,10 +542,10 @@ async fn status_snapshot_shows_active_user_defined_profile() { set_workspace_cwd(&mut config, test_path_buf("/workspace/tests").abs()); config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::read_only(), - Some(ActivePermissionProfile::new("locked")), - ) + ActivePermissionProfile::new("locked"), + )) .expect("set permission profile"); let usage = TokenUsage::default(); @@ -645,12 +640,10 @@ async fn status_snapshot_shows_auto_review_permissions() { config.approvals_reviewer = ApprovalsReviewer::AutoReview; config .permissions - .set_permission_profile_from_session_snapshot( + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( PermissionProfile::workspace_write(), - Some(ActivePermissionProfile::new( - BUILT_IN_PERMISSION_PROFILE_WORKSPACE, - )), - ) + ActivePermissionProfile::new(BUILT_IN_PERMISSION_PROFILE_WORKSPACE), + )) .expect("set permission profile"); let usage = TokenUsage::default();