config: own layer provenance types (#29722)

## Why

Config layer provenance describes how effective configuration was
assembled, so it belongs with the config loader rather than in
app-server's serialized API types.

## What changed

- Moved `ConfigLayerSource`, `ConfigLayerMetadata`, and `ConfigLayer`
ownership into `codex-config`.
- Kept app-server's wire payloads unchanged and added explicit
conversions at the app boundary.
- Removed lower-level app-server-protocol dependencies from config
consumers.

## Stack

This is PR 3 of 6, stacked on [PR
#29721](https://github.com/openai/codex/pull/29721). Review only the
delta from `codex/split-auth-domain-types`. Next: [PR
#29723](https://github.com/openai/codex/pull/29723).

## Validation

- `codex-config` coverage passed.
- App-server config-manager and config RPC coverage passed.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-23 21:03:04 -07:00
committed by GitHub
Unverified
parent 4fe02f4fcf
commit 1d65ccabd5
36 changed files with 206 additions and 72 deletions
+73 -1
View File
@@ -1,4 +1,76 @@
use codex_app_server_protocol::ConfigLayerSource;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde_json::Value as JsonValue;
/// Provenance for one layer in the effective Codex configuration.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigLayerSource {
/// Managed preferences delivered by MDM.
Mdm { domain: String, key: String },
/// Host-wide configuration loaded from a file.
System { file: AbsolutePathBuf },
/// Configuration delivered by an enterprise cloud bundle.
EnterpriseManaged { id: String, name: String },
/// User configuration, optionally augmented by a selected profile.
User {
file: AbsolutePathBuf,
profile: Option<String>,
},
/// Configuration loaded from a project's `.codex` directory.
Project { dot_codex_folder: AbsolutePathBuf },
/// Overrides supplied for the current session.
SessionFlags,
/// Legacy managed configuration loaded from a file.
LegacyManagedConfigTomlFromFile { file: AbsolutePathBuf },
/// Legacy managed configuration delivered by MDM.
LegacyManagedConfigTomlFromMdm,
}
impl ConfigLayerSource {
/// A setting from a layer with a higher precedence overrides a setting
/// from a layer with a lower precedence.
pub fn precedence(&self) -> i16 {
match self {
ConfigLayerSource::Mdm { .. } => 0,
ConfigLayerSource::System { .. } => 10,
ConfigLayerSource::EnterpriseManaged { .. } => 15,
ConfigLayerSource::User { profile, .. } => {
if profile.is_some() {
21
} else {
20
}
}
ConfigLayerSource::Project { .. } => 25,
ConfigLayerSource::SessionFlags => 30,
ConfigLayerSource::LegacyManagedConfigTomlFromFile { .. } => 40,
ConfigLayerSource::LegacyManagedConfigTomlFromMdm => 50,
}
}
}
/// Compares [`ConfigLayerSource`] by precedence, so `A < B` means settings
/// from layer `A` will be overridden by settings from layer `B`.
impl PartialOrd for ConfigLayerSource {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.precedence().cmp(&other.precedence()))
}
}
/// Identity and version information for a configuration layer.
#[derive(Debug, Clone, PartialEq)]
pub struct ConfigLayerMetadata {
pub name: ConfigLayerSource,
pub version: String,
}
/// A materialized configuration layer and its provenance.
#[derive(Debug, Clone, PartialEq)]
pub struct ConfigLayer {
pub name: ConfigLayerSource,
pub version: String,
pub config: JsonValue,
pub disabled_reason: Option<String>,
}
pub fn format_config_layer_source(source: &ConfigLayerSource, config_toml_file: &str) -> String {
match source {
+1 -1
View File
@@ -2,10 +2,10 @@
//! rendering them in a user-friendly way.
use crate::ConfigLayerEntry;
use crate::ConfigLayerSource;
use crate::ConfigLayerStack;
use crate::ConfigLayerStackOrdering;
use crate::format_config_layer_source;
use codex_app_server_protocol::ConfigLayerSource;
use codex_utils_absolute_path::AbsolutePathBufGuard;
use serde::de::DeserializeOwned;
use serde_path_to_error::Path as SerdePath;
+1 -1
View File
@@ -1,4 +1,4 @@
use codex_app_server_protocol::ConfigLayerMetadata;
use crate::ConfigLayerMetadata;
use serde_json::Value as JsonValue;
use sha2::Digest;
use sha2::Sha256;
+3 -1
View File
@@ -44,10 +44,12 @@ pub use cloud_config_layers::CloudConfigFragment;
pub use cloud_config_layers::CloudConfigFragmentSource;
pub use cloud_config_layers::CloudConfigLayerError;
pub use cloud_config_layers::cloud_config_layers_from_fragments;
pub use codex_app_server_protocol::ConfigLayerSource;
pub use codex_protocol::config_types::ProfileV2Name;
pub use codex_protocol::config_types::ProfileV2NameParseError;
pub use codex_utils_absolute_path::AbsolutePathBuf;
pub use config_layer_source::ConfigLayer;
pub use config_layer_source::ConfigLayerMetadata;
pub use config_layer_source::ConfigLayerSource;
pub use config_layer_source::format_config_layer_source;
pub use config_requirements::AppRequirementToml;
pub use config_requirements::AppToolRequirementToml;
+1 -1
View File
@@ -7,6 +7,7 @@ mod tests;
use self::layer_io::LoadedConfigLayers;
use crate::CONFIG_TOML_FILE;
use crate::CloudConfigBundleLayers;
use crate::ConfigLayerSource;
use crate::ProfileV2Name;
use crate::RequirementsLayerEntry;
use crate::compose_requirements;
@@ -31,7 +32,6 @@ use crate::strict_config::ignored_toml_value_field;
use crate::strict_config::unknown_feature_toml_value_field;
use crate::thread_config::ThreadConfigContext;
use crate::thread_config::ThreadConfigLoader;
use codex_app_server_protocol::ConfigLayerSource;
use codex_file_system::ExecutorFileSystem;
use codex_git_utils::resolve_root_git_project_for_trust;
use codex_protocol::config_types::ApprovalsReviewer;
+3 -3
View File
@@ -6,10 +6,10 @@ use super::fingerprint::version_for_toml;
use super::key_aliases::normalized_with_key_aliases;
use super::merge::merge_toml_values;
use crate::CloudConfigBundleLoader;
use crate::ConfigLayer;
use crate::ConfigLayerMetadata;
use crate::ConfigLayerSource;
use crate::ProfileV2Name;
use codex_app_server_protocol::ConfigLayer;
use codex_app_server_protocol::ConfigLayerMetadata;
use codex_app_server_protocol::ConfigLayerSource;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde_json::Value as JsonValue;
use std::collections::HashMap;
+1 -1
View File
@@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use codex_app_server_protocol::ConfigLayerSource;
use crate::ConfigLayerSource;
use codex_model_provider_info::ModelProviderInfo;
use codex_utils_absolute_path::AbsolutePathBuf;
use thiserror::Error;
-11
View File
@@ -920,17 +920,6 @@ pub struct SandboxWorkspaceWrite {
pub exclude_slash_tmp: bool,
}
impl From<SandboxWorkspaceWrite> for codex_app_server_protocol::SandboxSettings {
fn from(sandbox_workspace_write: SandboxWorkspaceWrite) -> Self {
Self {
writable_roots: sandbox_workspace_write.writable_roots,
network_access: Some(sandbox_workspace_write.network_access),
exclude_tmpdir_env_var: Some(sandbox_workspace_write.exclude_tmpdir_env_var),
exclude_slash_tmp: Some(sandbox_workspace_write.exclude_slash_tmp),
}
}
}
/// Policy for building the `env` when spawning a process via shell-like tools.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
#[schemars(deny_unknown_fields)]