core: derive active permission profiles (#18277)

## Why

`Permissions` should not store a separate `PermissionProfile` that can
drift from the constrained `SandboxPolicy` and network settings. The
active profile needs to be derived from the same constrained values that
already honor `requirements.toml`.

## What changed

This adds derivation of the active `PermissionProfile` from the
constrained runtime permission settings and exposes that derived value
through config snapshots and thread state. The app-server can then
report the active profile without introducing a second source of truth.

## Verification

- `cargo test -p codex-core --test all permissions_messages --
--nocapture`
- `cargo test -p codex-core --test all request_permissions --
--nocapture`



























---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18277).
* #18288
* #18287
* #18286
* #18285
* #18284
* #18283
* #18282
* #18281
* #18280
* #18279
* #18278
* __->__ #18277
This commit is contained in:
Michael Bolin
2026-04-21 22:11:40 -07:00
committed by GitHub
Unverified
parent 51fdc35945
commit 0fef35dc3a
4 changed files with 27 additions and 0 deletions
@@ -10207,6 +10207,11 @@ mod tests {
approval_policy: codex_protocol::protocol::AskForApproval::OnRequest,
approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User,
sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
permission_profile:
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
std::path::Path::new("/tmp"),
),
cwd: test_path_buf("/tmp").abs(),
ephemeral: false,
reasoning_effort: None,
+2
View File
@@ -11,6 +11,7 @@ use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
use codex_protocol::mcp::CallToolResult;
use codex_protocol::models::ContentItem;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort;
@@ -42,6 +43,7 @@ pub struct ThreadConfigSnapshot {
pub approval_policy: AskForApproval,
pub approvals_reviewer: ApprovalsReviewer,
pub sandbox_policy: SandboxPolicy,
pub permission_profile: PermissionProfile,
pub cwd: AbsolutePathBuf,
pub ephemeral: bool,
pub reasoning_effort: Option<ReasoningEffort>,
+12
View File
@@ -78,6 +78,7 @@ use codex_protocol::config_types::Verbosity;
use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::permissions::FileSystemSandboxPolicy;
@@ -216,6 +217,17 @@ pub struct Permissions {
pub windows_sandbox_private_desktop: bool,
}
impl Permissions {
/// Effective runtime permissions after config requirements and runtime
/// readable-root additions have been applied.
pub fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions(
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
}
}
/// Application configuration loaded from disk and merged with overrides.
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
+8
View File
@@ -90,6 +90,13 @@ impl SessionConfiguration {
&self.codex_home
}
pub(super) fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions(
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
}
pub(super) fn thread_config_snapshot(&self) -> ThreadConfigSnapshot {
ThreadConfigSnapshot {
model: self.collaboration_mode.model().to_string(),
@@ -98,6 +105,7 @@ impl SessionConfiguration {
approval_policy: self.approval_policy.value(),
approvals_reviewer: self.approvals_reviewer,
sandbox_policy: self.sandbox_policy.get().clone(),
permission_profile: self.permission_profile(),
cwd: self.cwd.clone(),
ephemeral: self.original_config_do_not_use.ephemeral,
reasoning_effort: self.collaboration_mode.reasoning_effort(),