mirror of
https://github.com/openai/codex.git
synced 2026-05-03 10:56:37 +00:00
app-server: accept permission profile overrides (#18279)
## Why `PermissionProfile` is becoming the canonical permissions shape shared by core and app-server. After app-server responses expose the active profile, clients need to be able to send that same shape back when starting, resuming, forking, or overriding a turn instead of translating through the legacy `sandbox`/`sandboxPolicy` shorthands. This still needs to preserve the existing requirements/platform enforcement model. A profile-shaped request can be downgraded or rejected by constraints, but the server should keep the user's elevated-access intent for project trust decisions. Turn-level profile overrides also need to retain existing read protections, including deny-read entries and bounded glob-scan metadata, so a permission override cannot accidentally drop configured protections such as `**/*.env = deny`. ## What changed - Adds optional `permissionProfile` request fields to `thread/start`, `thread/resume`, `thread/fork`, and `turn/start`. - Rejects ambiguous requests that specify both `permissionProfile` and the legacy `sandbox`/`sandboxPolicy` fields, including running-thread resume requests. - Converts profile-shaped overrides into core runtime filesystem/network permissions while continuing to derive the constrained legacy sandbox projection used by existing execution paths. - Preserves project-trust intent for profile overrides that are equivalent to workspace-write or full-access sandbox requests. - Preserves existing deny-read entries and `globScanMaxDepth` when applying turn-level `permissionProfile` overrides. - Updates app-server docs plus generated JSON/TypeScript schema fixtures and regression coverage. ## Verification - `cargo test -p codex-app-server-protocol schema_fixtures` - `cargo test -p codex-core session_configuration_apply_permission_profile_preserves_existing_deny_read_entries` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18279). * #18288 * #18287 * #18286 * #18285 * #18284 * #18283 * #18282 * #18281 * #18280 * __->__ #18279
This commit is contained in:
@@ -318,6 +318,13 @@ impl PermissionProfile {
|
||||
self.file_system_sandbox_policy()
|
||||
.to_legacy_sandbox_policy(self.network_sandbox_policy(), cwd)
|
||||
}
|
||||
|
||||
pub fn to_runtime_permissions(&self) -> (FileSystemSandboxPolicy, NetworkSandboxPolicy) {
|
||||
(
|
||||
self.file_system_sandbox_policy(),
|
||||
self.network_sandbox_policy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NetworkSandboxPolicy> for NetworkPermissions {
|
||||
|
||||
@@ -38,6 +38,7 @@ use crate::message_history::HistoryEntry;
|
||||
use crate::models::BaseInstructions;
|
||||
use crate::models::ContentItem;
|
||||
use crate::models::MessagePhase;
|
||||
use crate::models::PermissionProfile;
|
||||
use crate::models::ResponseInputItem;
|
||||
use crate::models::ResponseItem;
|
||||
use crate::models::WebSearchAction;
|
||||
@@ -442,6 +443,79 @@ pub enum Op {
|
||||
responsesapi_client_metadata: Option<HashMap<String, String>>,
|
||||
},
|
||||
|
||||
/// Similar to [`Op::UserInput`], but first applies persistent turn-context
|
||||
/// overrides in the same queued operation. This preserves submission order
|
||||
/// and prevents the input from starting if the overrides are rejected.
|
||||
UserInputWithTurnContext {
|
||||
/// User input items, see `InputItem`
|
||||
items: Vec<UserInput>,
|
||||
/// Optional turn-scoped environment selections.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
environments: Option<Vec<TurnEnvironmentSelection>>,
|
||||
/// Optional JSON Schema used to constrain the final assistant message for this turn.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
final_output_json_schema: Option<Value>,
|
||||
/// Optional turn-scoped Responses API `client_metadata`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
responsesapi_client_metadata: Option<HashMap<String, String>>,
|
||||
|
||||
/// Updated `cwd` for sandbox/tool calls.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
cwd: Option<PathBuf>,
|
||||
|
||||
/// Updated command approval policy.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
approval_policy: Option<AskForApproval>,
|
||||
|
||||
/// Updated approval reviewer for future approval prompts.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
approvals_reviewer: Option<ApprovalsReviewer>,
|
||||
|
||||
/// Updated sandbox policy for tool calls.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sandbox_policy: Option<SandboxPolicy>,
|
||||
|
||||
/// Updated permissions profile for tool calls.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
permission_profile: Option<PermissionProfile>,
|
||||
|
||||
/// Updated Windows sandbox mode for tool execution.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
|
||||
/// Updated model slug. When set, the model info is derived
|
||||
/// automatically.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
model: Option<String>,
|
||||
|
||||
/// Updated reasoning effort (honored only for reasoning-capable models).
|
||||
///
|
||||
/// Use `Some(Some(_))` to set a specific effort, `Some(None)` to clear
|
||||
/// the effort, or `None` to leave the existing value unchanged.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
effort: Option<Option<ReasoningEffortConfig>>,
|
||||
|
||||
/// Updated reasoning summary preference (honored only for reasoning-capable models).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
summary: Option<ReasoningSummaryConfig>,
|
||||
|
||||
/// Updated service tier preference for future turns.
|
||||
///
|
||||
/// Use `Some(Some(_))` to set a specific tier, `Some(None)` to clear the
|
||||
/// preference, or `None` to leave the existing value unchanged.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
service_tier: Option<Option<ServiceTier>>,
|
||||
|
||||
/// EXPERIMENTAL - set a pre-set collaboration mode.
|
||||
/// Takes precedence over model, effort, and developer instructions if set.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
collaboration_mode: Option<CollaborationMode>,
|
||||
|
||||
/// Updated personality preference.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
personality: Option<Personality>,
|
||||
},
|
||||
|
||||
/// Similar to [`Op::UserInput`], but contains additional context required
|
||||
/// for a turn of a [`crate::codex_thread::CodexThread`].
|
||||
UserTurn {
|
||||
@@ -532,6 +606,10 @@ pub enum Op {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sandbox_policy: Option<SandboxPolicy>,
|
||||
|
||||
/// Updated permissions profile for tool calls.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
permission_profile: Option<PermissionProfile>,
|
||||
|
||||
/// Updated Windows sandbox mode for tool execution.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
@@ -800,6 +878,7 @@ impl Op {
|
||||
Self::RealtimeConversationClose => "realtime_conversation_close",
|
||||
Self::RealtimeConversationListVoices => "realtime_conversation_list_voices",
|
||||
Self::UserInput { .. } => "user_input",
|
||||
Self::UserInputWithTurnContext { .. } => "user_input_with_turn_context",
|
||||
Self::UserTurn { .. } => "user_turn",
|
||||
Self::InterAgentCommunication { .. } => "inter_agent_communication",
|
||||
Self::OverrideTurnContext { .. } => "override_turn_context",
|
||||
|
||||
Reference in New Issue
Block a user