diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst index a3cf0d5717..05e0c041b4 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst differ diff --git a/codex-rs/app-server/src/request_processors/feedback_processor.rs b/codex-rs/app-server/src/request_processors/feedback_processor.rs index ce9222419e..ee4381c0f1 100644 --- a/codex-rs/app-server/src/request_processors/feedback_processor.rs +++ b/codex-rs/app-server/src/request_processors/feedback_processor.rs @@ -703,6 +703,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: (*model).to_string(), diff --git a/codex-rs/app-server/src/request_processors/thread_processor_tests.rs b/codex-rs/app-server/src/request_processors/thread_processor_tests.rs index a50145cca7..f3bb53d46b 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor_tests.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor_tests.rs @@ -108,6 +108,7 @@ mod persisted_resume_approval_policy_tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-5".to_string(), diff --git a/codex-rs/core/src/context_manager/history_tests.rs b/codex-rs/core/src/context_manager/history_tests.rs index 9a3164154a..666c708ef7 100644 --- a/codex-rs/core/src/context_manager/history_tests.rs +++ b/codex-rs/core/src/context_manager/history_tests.rs @@ -309,6 +309,7 @@ fn reference_context_item() -> TurnContextItem { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-test".to_string(), diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index e581569dfe..d801e10320 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -231,6 +231,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -278,6 +279,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -1341,6 +1343,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -1430,6 +1433,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -1462,6 +1466,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -1592,6 +1597,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: current_model.to_string(), @@ -1719,6 +1725,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -1889,6 +1896,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index a5573b660e..6f03b45952 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3497,6 +3497,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() { approvals_reviewer: None, sandbox_policy: turn_context.sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), @@ -9549,6 +9550,27 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy() ); } +#[tokio::test] +async fn turn_context_item_stores_active_permission_profile() { + let (_session, mut turn_context) = make_session_and_context().await; + let active_permission_profile = ActivePermissionProfile::read_only(); + let config = Arc::make_mut(&mut turn_context.config); + config + .permissions + .set_permission_profile_from_session_snapshot(PermissionProfileSnapshot::active( + PermissionProfile::read_only(), + active_permission_profile.clone(), + )) + .expect("test setup should allow updating permission profile"); + + assert_eq!( + turn_context + .to_turn_context_item() + .active_permission_profile, + Some(active_permission_profile) + ); +} + #[tokio::test] async fn turn_context_item_stores_split_file_system_sandbox_policy_when_different() { let (_session, mut turn_context) = make_session_and_context().await; diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 042ebf3d3f..8366cd94aa 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -478,6 +478,7 @@ impl TurnContext { approvals_reviewer: Some(self.config.approvals_reviewer), sandbox_policy: self.sandbox_policy(), permission_profile: Some(self.permission_profile()), + active_permission_profile: self.config.permissions.active_permission_profile(), network: self.turn_context_network_item(), file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(), model: self.model_info.slug.clone(), diff --git a/codex-rs/core/tests/suite/resume_warning.rs b/codex-rs/core/tests/suite/resume_warning.rs index 98333f1d8a..a025192822 100644 --- a/codex-rs/core/tests/suite/resume_warning.rs +++ b/codex-rs/core/tests/suite/resume_warning.rs @@ -38,6 +38,7 @@ fn resume_history( approvals_reviewer: None, sandbox_policy: config.legacy_sandbox_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: previous_model.to_string(), diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 9dffc109c3..b416c77776 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -3039,6 +3039,10 @@ pub struct TurnContextItem { pub sandbox_policy: SandboxPolicy, #[serde(default, skip_serializing_if = "Option::is_none")] pub permission_profile: Option, + /// Built-in or named profile that produced `permission_profile`, when known. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub active_permission_profile: Option, #[serde(skip_serializing_if = "Option::is_none")] pub network: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -5783,6 +5787,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, permission_profile: None, + active_permission_profile: None, network: Some(TurnContextNetworkItem { allowed_domains: vec!["api.example.com".to_string()], denied_domains: vec!["blocked.example.com".to_string()], diff --git a/codex-rs/rollout/src/recorder_tests.rs b/codex-rs/rollout/src/recorder_tests.rs index 2e39535a94..33bb163923 100644 --- a/codex-rs/rollout/src/recorder_tests.rs +++ b/codex-rs/rollout/src/recorder_tests.rs @@ -1704,6 +1704,7 @@ async fn resume_candidate_matches_cwd_reads_latest_turn_context() -> std::io::Re approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "test-model".to_string(), diff --git a/codex-rs/state/src/extract.rs b/codex-rs/state/src/extract.rs index 978b4e9dee..989075a23b 100644 --- a/codex-rs/state/src/extract.rs +++ b/codex-rs/state/src/extract.rs @@ -434,6 +434,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-5".to_string(), @@ -480,6 +481,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, permission_profile: Some(permission_profile.clone()), + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-5".to_string(), @@ -522,6 +524,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-5".to_string(), @@ -561,6 +564,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "gpt-5".to_string(), diff --git a/codex-rs/thread-store/src/local/mod.rs b/codex-rs/thread-store/src/local/mod.rs index 39c6de844e..f189f73523 100644 --- a/codex-rs/thread-store/src/local/mod.rs +++ b/codex-rs/thread-store/src/local/mod.rs @@ -926,6 +926,7 @@ mod tests { approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: model.to_string(), diff --git a/codex-rs/thread-store/src/local/model_context_tests.rs b/codex-rs/thread-store/src/local/model_context_tests.rs index 3e43af78c4..51b1fd69d9 100644 --- a/codex-rs/thread-store/src/local/model_context_tests.rs +++ b/codex-rs/thread-store/src/local/model_context_tests.rs @@ -603,6 +603,7 @@ fn turn_context(root: &Path, turn_id: &str) -> RolloutItem { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "test-model".to_string(), diff --git a/codex-rs/thread-store/src/local/rollout_migration_tests.rs b/codex-rs/thread-store/src/local/rollout_migration_tests.rs index 174bdf060e..741383267c 100644 --- a/codex-rs/thread-store/src/local/rollout_migration_tests.rs +++ b/codex-rs/thread-store/src/local/rollout_migration_tests.rs @@ -1398,6 +1398,7 @@ async fn migration_compacts_subagent_prefix_and_does_not_project_it() { approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), permission_profile: None, + active_permission_profile: None, network: None, file_system_sandbox_policy: None, model: "test-model".to_string(),