From 9c3abcd46c2c4d753ab4c227cc98303705cb893d Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Sun, 26 Apr 2026 15:10:53 -0700 Subject: [PATCH] [codex] Move config loading into codex-config (#19487) ## Why Config loading had become split across crates: `codex-config` owned the config types and merge logic, while `codex-core` still owned the loader that assembled the layer stack. This change consolidates that responsibility in `codex-config`, so the crate that defines config behavior also owns how configs are discovered and loaded. To make that move possible without reintroducing the old dependency cycle, the shell-environment policy types and helpers that `codex-exec-server` needs now live in `codex-protocol` instead of flowing through `codex-config`. This also makes the migrated loader tests more deterministic on machines that already have managed or system Codex config installed by letting tests override the system config and requirements paths instead of reading the host's `/etc/codex`. ## What Changed - moved the config loader implementation from `codex-core` into `codex-config::loader` and deleted the old `core::config_loader` module instead of leaving a compatibility shim - moved shell-environment policy types and helpers into `codex-protocol`, then updated `codex-exec-server` and other downstream crates to import them from their new home - updated downstream callers to use loader/config APIs from `codex-config` - added test-only loader overrides for system config and requirements paths so loader-focused tests do not depend on host-managed config state - cleaned up now-unused dependency entries and platform-specific cfgs that were surfaced by post-push CI ## Testing - `cargo test -p codex-config` - `cargo test -p codex-core config_loader_tests::` - `cargo test -p codex-protocol -p codex-exec-server -p codex-cloud-requirements -p codex-rmcp-client --lib` - `cargo test --lib -p codex-app-server-client -p codex-exec` - `cargo test --no-run --lib -p codex-app-server` - `cargo test -p codex-linux-sandbox --lib` - `cargo shear` - `just bazel-lock-check` ## Notes - I did not chase unrelated full-suite failures outside the migrated loader surface. - `cargo test -p codex-core --lib` still hits unrelated proxy-sensitive failures on this machine, and Windows CI still shows unrelated long-running/timeouting test noise outside the loader migration itself. --- codex-rs/Cargo.lock | 12 +- codex-rs/app-server-client/src/lib.rs | 4 +- .../app-server/src/codex_message_processor.rs | 10 +- codex-rs/app-server/src/config_api.rs | 70 ++++------ codex-rs/app-server/src/config_manager.rs | 8 +- .../app-server/src/config_manager_service.rs | 10 +- .../src/config_manager_service_tests.rs | 6 +- codex-rs/app-server/src/in_process.rs | 4 +- codex-rs/app-server/src/lib.rs | 8 +- codex-rs/app-server/src/main.rs | 2 +- .../src/message_processor/tracing_tests.rs | 4 +- .../suite/v2/experimental_feature_list.rs | 2 +- .../app-server/tests/suite/v2/mcp_resource.rs | 4 +- .../tests/suite/v2/remote_thread_store.rs | 4 +- .../app-server/tests/suite/v2/thread_start.rs | 2 +- codex-rs/cli/src/main.rs | 4 +- codex-rs/cloud-requirements/src/lib.rs | 12 +- codex-rs/config/Cargo.toml | 12 ++ codex-rs/config/src/lib.rs | 2 +- .../src/loader}/README.md | 8 +- .../src/loader}/layer_io.rs | 6 +- .../src/loader}/macos.rs | 6 +- .../src/loader}/mod.rs | 131 +++++++----------- codex-rs/config/src/state.rs | 24 ++-- codex-rs/config/src/types.rs | 63 +-------- codex-rs/core/Cargo.toml | 10 -- codex-rs/core/src/agent/role.rs | 8 +- codex-rs/core/src/agent/role_tests.rs | 2 +- codex-rs/core/src/agents_md.rs | 8 +- codex-rs/core/src/config/agent_roles.rs | 4 +- .../config_loader_tests.rs} | 124 +++++++++-------- codex-rs/core/src/config/config_tests.rs | 84 ++++++----- codex-rs/core/src/config/mod.rs | 49 ++++--- .../core/src/config/network_proxy_spec.rs | 2 +- .../src/config/network_proxy_spec_tests.rs | 4 +- codex-rs/core/src/connectors.rs | 2 +- codex-rs/core/src/connectors_tests.rs | 12 +- codex-rs/core/src/exec_env.rs | 15 +- codex-rs/core/src/exec_env_tests.rs | 2 +- codex-rs/core/src/exec_policy.rs | 4 +- codex-rs/core/src/exec_policy_tests.rs | 16 +-- codex-rs/core/src/guardian/tests.rs | 16 +-- codex-rs/core/src/lib.rs | 1 - codex-rs/core/src/network_proxy_loader.rs | 10 +- codex-rs/core/src/plugins/manager.rs | 2 +- codex-rs/core/src/plugins/manager_tests.rs | 8 +- codex-rs/core/src/session/handlers.rs | 6 +- codex-rs/core/src/session/mod.rs | 2 +- codex-rs/core/src/session/tests.rs | 18 +-- .../core/src/session/tests/guardian_tests.rs | 6 +- .../src/tools/handlers/multi_agents_tests.rs | 2 +- .../core/src/unified_exec/process_manager.rs | 2 +- .../src/unified_exec/process_manager_tests.rs | 2 +- codex-rs/core/tests/suite/approvals.rs | 12 +- .../core/tests/suite/deprecation_notice.rs | 8 +- codex-rs/core/tests/suite/hooks.rs | 12 +- .../core/tests/suite/permissions_messages.rs | 2 +- codex-rs/exec-server/Cargo.toml | 1 - codex-rs/exec-server/src/local_process.rs | 8 +- codex-rs/exec-server/src/protocol.rs | 2 +- codex-rs/exec/Cargo.toml | 1 + codex-rs/exec/src/lib.rs | 6 +- codex-rs/linux-sandbox/Cargo.toml | 1 - .../linux-sandbox/tests/suite/landlock.rs | 2 +- .../tests/suite/managed_proxy.rs | 2 +- codex-rs/protocol/Cargo.toml | 1 + codex-rs/protocol/src/config_types.rs | 61 ++++++++ codex-rs/protocol/src/lib.rs | 1 + .../src/shell_environment.rs | 7 +- .../rmcp-client/src/stdio_server_launcher.rs | 8 +- 70 files changed, 483 insertions(+), 491 deletions(-) rename codex-rs/{core/src/config_loader => config/src/loader}/README.md (93%) rename codex-rs/{core/src/config_loader => config/src/loader}/layer_io.rs (96%) rename codex-rs/{core/src/config_loader => config/src/loader}/macos.rs (97%) rename codex-rs/{core/src/config_loader => config/src/loader}/mod.rs (92%) rename codex-rs/core/src/{config_loader/tests.rs => config/config_loader_tests.rs} (95%) rename codex-rs/{config => protocol}/src/shell_environment.rs (95%) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index fd4ed6d8d..fbab962cb 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2286,15 +2286,20 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", + "base64 0.22.1", "codex-app-server-protocol", + "codex-exec-server", "codex-execpolicy", "codex-features", + "codex-git-utils", "codex-model-provider-info", "codex-network-proxy", "codex-protocol", "codex-utils-absolute-path", "codex-utils-path", + "core-foundation 0.9.4", "dns-lookup", + "dunce", "futures", "gethostname", "libc", @@ -2318,6 +2323,7 @@ dependencies = [ "tracing", "wildmatch", "winapi-util", + "windows-sys 0.52.0", ] [[package]] @@ -2398,7 +2404,6 @@ dependencies = [ "codex-utils-string", "codex-utils-template", "codex-windows-sandbox", - "core-foundation 0.9.4", "core_test_support", "csv", "ctor 0.6.3", @@ -2449,7 +2454,6 @@ dependencies = [ "walkdir", "which 8.0.0", "whoami", - "windows-sys 0.52.0", "wiremock", "zstd 0.13.3", ] @@ -2559,6 +2563,7 @@ dependencies = [ "codex-apply-patch", "codex-arg0", "codex-cloud-requirements", + "codex-config", "codex-core", "codex-feedback", "codex-git-utils", @@ -2602,7 +2607,6 @@ dependencies = [ "bytes", "codex-app-server-protocol", "codex-client", - "codex-config", "codex-protocol", "codex-sandboxing", "codex-test-binary-support", @@ -2780,7 +2784,6 @@ version = "0.0.0" dependencies = [ "cc", "clap", - "codex-config", "codex-core", "codex-protocol", "codex-sandboxing", @@ -3117,6 +3120,7 @@ dependencies = [ "tracing", "ts-rs", "uuid", + "wildmatch", ] [[package]] diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 1429fa26c..e1614c32d 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -41,12 +41,12 @@ use codex_app_server_protocol::Result as JsonRpcResult; use codex_app_server_protocol::ServerNotification; use codex_app_server_protocol::ServerRequest; use codex_arg0::Arg0DispatchPaths; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; use codex_config::NoopThreadConfigLoader; use codex_config::RemoteThreadConfigLoader; use codex_config::ThreadConfigLoader; use codex_core::config::Config; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::LoaderOverrides; pub use codex_exec_server::EnvironmentManager; pub use codex_exec_server::EnvironmentManagerArgs; pub use codex_exec_server::ExecServerRuntimePaths; diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index d479de353..2c6e172f7 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -230,6 +230,9 @@ use codex_backend_client::AddCreditsNudgeCreditType as BackendAddCreditsNudgeCre use codex_backend_client::Client as BackendClient; use codex_chatgpt::connectors; use codex_chatgpt::workspace_settings; +use codex_config::CloudRequirementsLoadError; +use codex_config::CloudRequirementsLoadErrorCode; +use codex_config::loader::project_trust_key; use codex_config::types::McpServerTransportConfig; use codex_core::CodexThread; use codex_core::CodexThreadTurnContextOverrides; @@ -248,9 +251,6 @@ use codex_core::config::NetworkProxyAuditMetadata; use codex_core::config::ThreadStoreConfig; use codex_core::config::edit::ConfigEdit; use codex_core::config::edit::ConfigEditsBuilder; -use codex_core::config_loader::CloudRequirementsLoadError; -use codex_core::config_loader::CloudRequirementsLoadErrorCode; -use codex_core::config_loader::project_trust_key; use codex_core::exec::ExecCapturePolicy; use codex_core::exec::ExecExpiration; use codex_core::exec::ExecParams; @@ -10453,11 +10453,11 @@ mod tests { use chrono::Utc; use codex_app_server_protocol::ServerRequestPayload; use codex_app_server_protocol::ToolRequestUserInputParams; + use codex_config::CloudRequirementsLoader; + use codex_config::LoaderOverrides; use codex_config::SessionThreadConfig; use codex_config::StaticThreadConfigLoader; use codex_config::ThreadConfigSource; - use codex_core::config_loader::CloudRequirementsLoader; - use codex_core::config_loader::LoaderOverrides; use codex_model_provider_info::ModelProviderInfo; use codex_model_provider_info::WireApi; use codex_protocol::ThreadId; diff --git a/codex-rs/app-server/src/config_api.rs b/codex-rs/app-server/src/config_api.rs index 355b41543..e8bb82777 100644 --- a/codex-rs/app-server/src/config_api.rs +++ b/codex-rs/app-server/src/config_api.rs @@ -23,15 +23,15 @@ use codex_app_server_protocol::NetworkDomainPermission; use codex_app_server_protocol::NetworkRequirements; use codex_app_server_protocol::NetworkUnixSocketPermission; use codex_app_server_protocol::SandboxMode; +use codex_config::ConfigRequirementsToml; +use codex_config::HookEventsToml; +use codex_config::HookHandlerConfig as CoreHookHandlerConfig; +use codex_config::ManagedHooksRequirementsToml; +use codex_config::MatcherGroup as CoreMatcherGroup; +use codex_config::ResidencyRequirement as CoreResidencyRequirement; +use codex_config::SandboxModeRequirement as CoreSandboxModeRequirement; use codex_core::ThreadManager; use codex_core::config::Config; -use codex_core::config_loader::ConfigRequirementsToml; -use codex_core::config_loader::HookEventsToml; -use codex_core::config_loader::HookHandlerConfig as CoreHookHandlerConfig; -use codex_core::config_loader::ManagedHooksRequirementsToml; -use codex_core::config_loader::MatcherGroup as CoreMatcherGroup; -use codex_core::config_loader::ResidencyRequirement as CoreResidencyRequirement; -use codex_core::config_loader::SandboxModeRequirement as CoreSandboxModeRequirement; use codex_core::plugins::PluginId; use codex_core_plugins::loader::installed_plugin_telemetry_metadata; use codex_core_plugins::toggles::collect_plugin_enabled_candidates; @@ -377,20 +377,20 @@ fn map_residency_requirement_to_api( } fn map_network_requirements_to_api( - network: codex_core::config_loader::NetworkRequirementsToml, + network: codex_config::NetworkRequirementsToml, ) -> NetworkRequirements { let allowed_domains = network .domains .as_ref() - .and_then(codex_core::config_loader::NetworkDomainPermissionsToml::allowed_domains); + .and_then(codex_config::NetworkDomainPermissionsToml::allowed_domains); let denied_domains = network .domains .as_ref() - .and_then(codex_core::config_loader::NetworkDomainPermissionsToml::denied_domains); + .and_then(codex_config::NetworkDomainPermissionsToml::denied_domains); let allow_unix_sockets = network .unix_sockets .as_ref() - .map(codex_core::config_loader::NetworkUnixSocketPermissionsToml::allow_unix_sockets) + .map(codex_config::NetworkUnixSocketPermissionsToml::allow_unix_sockets) .filter(|entries| !entries.is_empty()); NetworkRequirements { @@ -427,28 +427,20 @@ fn map_network_requirements_to_api( } fn map_network_domain_permission_to_api( - permission: codex_core::config_loader::NetworkDomainPermissionToml, + permission: codex_config::NetworkDomainPermissionToml, ) -> NetworkDomainPermission { match permission { - codex_core::config_loader::NetworkDomainPermissionToml::Allow => { - NetworkDomainPermission::Allow - } - codex_core::config_loader::NetworkDomainPermissionToml::Deny => { - NetworkDomainPermission::Deny - } + codex_config::NetworkDomainPermissionToml::Allow => NetworkDomainPermission::Allow, + codex_config::NetworkDomainPermissionToml::Deny => NetworkDomainPermission::Deny, } } fn map_network_unix_socket_permission_to_api( - permission: codex_core::config_loader::NetworkUnixSocketPermissionToml, + permission: codex_config::NetworkUnixSocketPermissionToml, ) -> NetworkUnixSocketPermission { match permission { - codex_core::config_loader::NetworkUnixSocketPermissionToml::Allow => { - NetworkUnixSocketPermission::Allow - } - codex_core::config_loader::NetworkUnixSocketPermissionToml::None => { - NetworkUnixSocketPermission::None - } + codex_config::NetworkUnixSocketPermissionToml::Allow => NetworkUnixSocketPermission::Allow, + codex_config::NetworkUnixSocketPermissionToml::None => NetworkUnixSocketPermission::None, } } @@ -476,13 +468,13 @@ mod tests { use crate::config_manager::apply_runtime_feature_enablement; use codex_analytics::AnalyticsEventsClient; use codex_arg0::Arg0DispatchPaths; - use codex_core::config_loader::CloudRequirementsLoader; - use codex_core::config_loader::LoaderOverrides; - use codex_core::config_loader::NetworkDomainPermissionToml as CoreNetworkDomainPermissionToml; - use codex_core::config_loader::NetworkDomainPermissionsToml as CoreNetworkDomainPermissionsToml; - use codex_core::config_loader::NetworkRequirementsToml as CoreNetworkRequirementsToml; - use codex_core::config_loader::NetworkUnixSocketPermissionToml as CoreNetworkUnixSocketPermissionToml; - use codex_core::config_loader::NetworkUnixSocketPermissionsToml as CoreNetworkUnixSocketPermissionsToml; + use codex_config::CloudRequirementsLoader; + use codex_config::LoaderOverrides; + use codex_config::NetworkDomainPermissionToml as CoreNetworkDomainPermissionToml; + use codex_config::NetworkDomainPermissionsToml as CoreNetworkDomainPermissionsToml; + use codex_config::NetworkRequirementsToml as CoreNetworkRequirementsToml; + use codex_config::NetworkUnixSocketPermissionToml as CoreNetworkUnixSocketPermissionToml; + use codex_config::NetworkUnixSocketPermissionsToml as CoreNetworkUnixSocketPermissionsToml; use codex_features::Feature; use codex_login::AuthManager; use codex_login::CodexAuth; @@ -524,11 +516,9 @@ mod tests { CoreSandboxModeRequirement::ExternalSandbox, ]), remote_sandbox_config: None, - allowed_web_search_modes: Some(vec![ - codex_core::config_loader::WebSearchModeRequirement::Cached, - ]), + allowed_web_search_modes: Some(vec![codex_config::WebSearchModeRequirement::Cached]), guardian_policy_config: None, - feature_requirements: Some(codex_core::config_loader::FeatureRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: std::collections::BTreeMap::from([ ("apps".to_string(), false), ("personality".to_string(), true), @@ -794,11 +784,9 @@ mod tests { )]) .cloud_requirements(CloudRequirementsLoader::new(async { Ok(Some(ConfigRequirementsToml { - feature_requirements: Some( - codex_core::config_loader::FeatureRequirementsToml { - entries: BTreeMap::from([("apps".to_string(), false)]), - }, - ), + feature_requirements: Some(codex_config::FeatureRequirementsToml { + entries: BTreeMap::from([("apps".to_string(), false)]), + }), ..Default::default() })) })) diff --git a/codex-rs/app-server/src/config_manager.rs b/codex-rs/app-server/src/config_manager.rs index 43dd19004..399c0c9fa 100644 --- a/codex-rs/app-server/src/config_manager.rs +++ b/codex-rs/app-server/src/config_manager.rs @@ -1,12 +1,12 @@ use codex_arg0::Arg0DispatchPaths; use codex_cloud_requirements::cloud_requirements_loader; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigLayerStack; +use codex_config::LoaderOverrides; use codex_config::ThreadConfigLoader; +use codex_config::loader::load_config_layers_state; use codex_core::config::Config; use codex_core::config::ConfigOverrides; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::ConfigLayerStack; -use codex_core::config_loader::LoaderOverrides; -use codex_core::config_loader::load_config_layers_state; use codex_exec_server::LOCAL_FS; use codex_features::feature_for_key; use codex_login::AuthManager; diff --git a/codex-rs/app-server/src/config_manager_service.rs b/codex-rs/app-server/src/config_manager_service.rs index 0104429a4..ec4a1a680 100644 --- a/codex-rs/app-server/src/config_manager_service.rs +++ b/codex-rs/app-server/src/config_manager_service.rs @@ -12,16 +12,16 @@ use codex_app_server_protocol::MergeStrategy; use codex_app_server_protocol::OverriddenMetadata; use codex_app_server_protocol::WriteStatus; use codex_config::CONFIG_TOML_FILE; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::ConfigRequirementsToml; use codex_config::config_toml::ConfigToml; +use codex_config::merge_toml_values; use codex_core::config::deserialize_config_toml_with_base; use codex_core::config::edit::ConfigEdit; use codex_core::config::edit::ConfigEditsBuilder; use codex_core::config::validate_feature_requirements_for_config_toml; -use codex_core::config_loader::ConfigLayerEntry; -use codex_core::config_loader::ConfigLayerStack; -use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::config_loader::ConfigRequirementsToml; -use codex_core::config_loader::merge_toml_values; use codex_core::path_utils; use codex_core::path_utils::SymlinkWritePaths; use codex_core::path_utils::resolve_symlink_write_paths; diff --git a/codex-rs/app-server/src/config_manager_service_tests.rs b/codex-rs/app-server/src/config_manager_service_tests.rs index a871d8e43..02c76e3b5 100644 --- a/codex-rs/app-server/src/config_manager_service_tests.rs +++ b/codex-rs/app-server/src/config_manager_service_tests.rs @@ -4,9 +4,9 @@ use codex_app_server_protocol::AppConfig; use codex_app_server_protocol::AppToolApproval; use codex_app_server_protocol::AppsConfig; use codex_app_server_protocol::AskForApproval; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::FeatureRequirementsToml; -use codex_core::config_loader::LoaderOverrides; +use codex_config::CloudRequirementsLoader; +use codex_config::FeatureRequirementsToml; +use codex_config::LoaderOverrides; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; use std::collections::BTreeMap; diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index dac25b693..cc4e22e92 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -77,10 +77,10 @@ use codex_app_server_protocol::Result; use codex_app_server_protocol::ServerNotification; use codex_app_server_protocol::ServerRequest; use codex_arg0::Arg0DispatchPaths; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; use codex_config::ThreadConfigLoader; use codex_core::config::Config; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::LoaderOverrides; use codex_exec_server::EnvironmentManager; use codex_feedback::CodexFeedback; use codex_login::AuthManager; diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 64f487482..59e8cc982 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -1,12 +1,12 @@ #![deny(clippy::print_stdout, clippy::print_stderr)] use codex_arg0::Arg0DispatchPaths; +use codex_config::ConfigLayerStackOrdering; +use codex_config::LoaderOverrides; use codex_config::NoopThreadConfigLoader; use codex_config::RemoteThreadConfigLoader; use codex_config::ThreadConfigLoader; use codex_core::config::Config; -use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::config_loader::LoaderOverrides; use codex_exec_server::EnvironmentManagerArgs; use codex_features::Feature; use codex_login::AuthManager; @@ -42,11 +42,11 @@ use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::TextPosition as AppTextPosition; use codex_app_server_protocol::TextRange as AppTextRange; +use codex_config::ConfigLoadError; +use codex_config::TextRange as CoreTextRange; use codex_core::ExecPolicyError; use codex_core::check_execpolicy_for_warnings; use codex_core::config::find_codex_home; -use codex_core::config_loader::ConfigLoadError; -use codex_core::config_loader::TextRange as CoreTextRange; use codex_exec_server::EnvironmentManager; use codex_exec_server::ExecServerRuntimePaths; use codex_feedback::CodexFeedback; diff --git a/codex-rs/app-server/src/main.rs b/codex-rs/app-server/src/main.rs index 67098c2b3..1cb4bd9a8 100644 --- a/codex-rs/app-server/src/main.rs +++ b/codex-rs/app-server/src/main.rs @@ -6,7 +6,7 @@ use codex_app_server::PluginStartupTasks; use codex_app_server::run_main_with_transport_options; use codex_arg0::Arg0DispatchPaths; use codex_arg0::arg0_dispatch_or_else; -use codex_core::config_loader::LoaderOverrides; +use codex_config::LoaderOverrides; use codex_protocol::protocol::SessionSource; use codex_utils_cli::CliConfigOverrides; use std::path::PathBuf; diff --git a/codex-rs/app-server/src/message_processor/tracing_tests.rs b/codex-rs/app-server/src/message_processor/tracing_tests.rs index 7160b57d5..507d7865b 100644 --- a/codex-rs/app-server/src/message_processor/tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor/tracing_tests.rs @@ -27,10 +27,10 @@ use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::UserInput; use codex_arg0::Arg0DispatchPaths; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; use codex_core::config::Config; use codex_core::config::ConfigBuilder; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::LoaderOverrides; use codex_exec_server::EnvironmentManager; use codex_feedback::CodexFeedback; use codex_login::AuthManager; diff --git a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs index 30b4c0f32..57520a2d6 100644 --- a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs +++ b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs @@ -16,9 +16,9 @@ use codex_app_server_protocol::ExperimentalFeatureStage; use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; +use codex_config::LoaderOverrides; use codex_config::types::AuthCredentialsStoreMode; use codex_core::config::ConfigBuilder; -use codex_core::config_loader::LoaderOverrides; use codex_features::FEATURES; use codex_features::Stage; use pretty_assertions::assert_eq; diff --git a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs index a347d87fc..3b1a49557 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs @@ -20,10 +20,10 @@ use codex_app_server_protocol::RequestId; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; use codex_arg0::Arg0DispatchPaths; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; use codex_config::types::AuthCredentialsStoreMode; use codex_core::config::ConfigBuilder; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::LoaderOverrides; use codex_exec_server::EnvironmentManager; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; diff --git a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs index 7556f4cd1..27160bd78 100644 --- a/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs +++ b/codex-rs/app-server/tests/suite/v2/remote_thread_store.rs @@ -33,10 +33,10 @@ use codex_app_server_protocol::ThreadStartResponse; use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::UserInput as V2UserInput; use codex_arg0::Arg0DispatchPaths; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; use codex_config::NoopThreadConfigLoader; use codex_core::config::ConfigBuilder; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::LoaderOverrides; use codex_exec_server::EnvironmentManager; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index 3177003dd..f521d5509 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -20,9 +20,9 @@ use codex_app_server_protocol::ThreadStartedNotification; use codex_app_server_protocol::ThreadStatus; use codex_app_server_protocol::ThreadStatusChangedNotification; use codex_app_server_protocol::TurnEnvironmentParams; +use codex_config::loader::project_trust_key; use codex_config::types::AuthCredentialsStoreMode; use codex_core::config::set_project_trust_level; -use codex_core::config_loader::project_trust_key; use codex_exec_server::LOCAL_FS; use codex_git_utils::resolve_root_git_project_for_trust; use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR; diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 415769e36..9f465521d 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -830,7 +830,7 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> { codex_app_server::run_main_with_transport( arg0_paths.clone(), root_config_overrides, - codex_core::config_loader::LoaderOverrides::default(), + codex_config::LoaderOverrides::default(), analytics_default_enabled, transport, codex_protocol::protocol::SessionSource::VSCode, @@ -1551,7 +1551,7 @@ async fn run_interactive_tui( codex_tui::run_main( interactive, arg0_paths, - codex_core::config_loader::LoaderOverrides::default(), + codex_config::LoaderOverrides::default(), normalized_remote, remote_auth_token, ) diff --git a/codex-rs/cloud-requirements/src/lib.rs b/codex-rs/cloud-requirements/src/lib.rs index 1d9975f12..86a12e7d1 100644 --- a/codex-rs/cloud-requirements/src/lib.rs +++ b/codex-rs/cloud-requirements/src/lib.rs @@ -15,11 +15,11 @@ use chrono::DateTime; use chrono::Duration as ChronoDuration; use chrono::Utc; use codex_backend_client::Client as BackendClient; +use codex_config::CloudRequirementsLoadError; +use codex_config::CloudRequirementsLoadErrorCode; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigRequirementsToml; use codex_config::types::AuthCredentialsStoreMode; -use codex_core::config_loader::CloudRequirementsLoadError; -use codex_core::config_loader::CloudRequirementsLoadErrorCode; -use codex_core::config_loader::CloudRequirementsLoader; -use codex_core::config_loader::ConfigRequirementsToml; use codex_core::util::backoff; use codex_login::AuthManager; use codex_login::CodexAuth; @@ -1314,10 +1314,10 @@ enabled = false assert_eq!( result, Some(ConfigRequirementsToml { - apps: Some(codex_core::config_loader::AppsRequirementsToml { + apps: Some(codex_config::AppsRequirementsToml { apps: BTreeMap::from([( "connector_5f3c8c41a1e54ad7a76272c89e2554fa".to_string(), - codex_core::config_loader::AppRequirementToml { + codex_config::AppRequirementToml { enabled: Some(false), }, )]), diff --git a/codex-rs/config/Cargo.toml b/codex-rs/config/Cargo.toml index 9df08b115..3c7e5a829 100644 --- a/codex-rs/config/Cargo.toml +++ b/codex-rs/config/Cargo.toml @@ -14,14 +14,18 @@ workspace = true [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } +base64 = { workspace = true } codex-app-server-protocol = { workspace = true } +codex-exec-server = { workspace = true } codex-execpolicy = { workspace = true } codex-features = { workspace = true } +codex-git-utils = { workspace = true } codex-model-provider-info = { workspace = true } codex-network-proxy = { workspace = true } codex-protocol = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-path = { workspace = true } +dunce = { workspace = true } futures = { workspace = true, features = ["alloc", "std"] } gethostname = { workspace = true } multimap = { workspace = true } @@ -44,8 +48,16 @@ wildmatch = { workspace = true } dns-lookup = { workspace = true } libc = { workspace = true } +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = "0.9" + [target.'cfg(target_os = "windows")'.dependencies] winapi-util = { workspace = true } +windows-sys = { version = "0.52", features = [ + "Win32_Foundation", + "Win32_System_Com", + "Win32_UI_Shell", +] } [dev-dependencies] pretty_assertions = { workspace = true } diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs index e3d95acb8..eb0e7713f 100644 --- a/codex-rs/config/src/lib.rs +++ b/codex-rs/config/src/lib.rs @@ -7,6 +7,7 @@ mod fingerprint; mod hook_config; mod host_name; mod key_aliases; +pub mod loader; mod marketplace_edit; mod mcp_edit; mod mcp_types; @@ -17,7 +18,6 @@ pub mod profile_toml; mod project_root_markers; mod requirements_exec_policy; pub mod schema; -pub mod shell_environment; mod skills_config; mod state; mod thread_config; diff --git a/codex-rs/core/src/config_loader/README.md b/codex-rs/config/src/loader/README.md similarity index 93% rename from codex-rs/core/src/config_loader/README.md rename to codex-rs/config/src/loader/README.md index 6ee445421..316027318 100644 --- a/codex-rs/core/src/config_loader/README.md +++ b/codex-rs/config/src/loader/README.md @@ -1,4 +1,4 @@ -# `codex-core` config loader +# `codex-config` loader This module is the canonical place to **load and describe Codex configuration layers** (user config, CLI/session overrides, managed config, and MDM-managed preferences) and to produce: @@ -8,7 +8,7 @@ This module is the canonical place to **load and describe Codex configuration la ## Public surface -Exported from `codex_core::config_loader`: +Exported from `codex_config::loader`: - `load_config_layers_state(fs, codex_home, cwd_opt, cli_overrides, overrides, cloud_requirements, thread_config_loader, host_name) -> ConfigLayerStack` - `ConfigLayerStack` @@ -41,8 +41,10 @@ computing the effective config and origins metadata. This is what Most callers want the effective config plus metadata: ```rust -use codex_core::config_loader::{CloudRequirementsLoader, LoaderOverrides, load_config_layers_state}; use codex_config::NoopThreadConfigLoader; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; +use codex_config::loader::load_config_layers_state; use codex_exec_server::LOCAL_FS; use codex_utils_absolute_path::AbsolutePathBuf; use toml::Value as TomlValue; diff --git a/codex-rs/core/src/config_loader/layer_io.rs b/codex-rs/config/src/loader/layer_io.rs similarity index 96% rename from codex-rs/core/src/config_loader/layer_io.rs rename to codex-rs/config/src/loader/layer_io.rs index 6bd9a9130..773a71f3b 100644 --- a/codex-rs/core/src/config_loader/layer_io.rs +++ b/codex-rs/config/src/loader/layer_io.rs @@ -1,10 +1,10 @@ -use super::LoaderOverrides; #[cfg(target_os = "macos")] use super::macos::ManagedAdminConfigLayer; #[cfg(target_os = "macos")] use super::macos::load_managed_admin_config_layer; -use codex_config::config_error_from_toml; -use codex_config::io_error_from_config_error; +use crate::diagnostics::config_error_from_toml; +use crate::diagnostics::io_error_from_config_error; +use crate::state::LoaderOverrides; use codex_exec_server::ExecutorFileSystem; use codex_utils_absolute_path::AbsolutePathBuf; use std::io; diff --git a/codex-rs/core/src/config_loader/macos.rs b/codex-rs/config/src/loader/macos.rs similarity index 97% rename from codex-rs/core/src/config_loader/macos.rs rename to codex-rs/config/src/loader/macos.rs index 977a09a9c..252542972 100644 --- a/codex-rs/core/src/config_loader/macos.rs +++ b/codex-rs/config/src/loader/macos.rs @@ -1,7 +1,7 @@ -use super::ConfigRequirementsToml; -use super::ConfigRequirementsWithSources; -use super::RequirementSource; use super::merge_requirements_with_remote_sandbox_config; +use crate::config_requirements::ConfigRequirementsToml; +use crate::config_requirements::ConfigRequirementsWithSources; +use crate::config_requirements::RequirementSource; use base64::Engine; use base64::prelude::BASE64_STANDARD; use core_foundation::base::TCFType; diff --git a/codex-rs/core/src/config_loader/mod.rs b/codex-rs/config/src/loader/mod.rs similarity index 92% rename from codex-rs/core/src/config_loader/mod.rs rename to codex-rs/config/src/loader/mod.rs index 4681aa075..e930e8b62 100644 --- a/codex-rs/core/src/config_loader/mod.rs +++ b/codex-rs/config/src/loader/mod.rs @@ -2,17 +2,29 @@ mod layer_io; #[cfg(target_os = "macos")] mod macos; -#[cfg(test)] -mod tests; - -use crate::config_loader::layer_io::LoadedConfigLayers; +use self::layer_io::LoadedConfigLayers; +use crate::CONFIG_TOML_FILE; +use crate::cloud_requirements::CloudRequirementsLoader; +use crate::config_requirements::ConfigRequirementsToml; +use crate::config_requirements::ConfigRequirementsWithSources; +use crate::config_requirements::RequirementSource; +use crate::config_requirements::SandboxModeRequirement; +use crate::config_toml::ConfigToml; +use crate::config_toml::ProjectConfig; +use crate::diagnostics::ConfigError; +use crate::diagnostics::config_error_from_toml; +use crate::diagnostics::first_layer_config_error_from_entries as typed_first_layer_config_error_from_entries; +use crate::diagnostics::io_error_from_config_error; +use crate::merge::merge_toml_values; +use crate::overrides::build_cli_overrides_layer; +use crate::project_root_markers::default_project_root_markers; +use crate::project_root_markers::project_root_markers_from_config; +use crate::state::ConfigLayerEntry; +use crate::state::ConfigLayerStack; +use crate::state::LoaderOverrides; +use crate::thread_config::ThreadConfigContext; +use crate::thread_config::ThreadConfigLoader; use codex_app_server_protocol::ConfigLayerSource; -use codex_config::CONFIG_TOML_FILE; -use codex_config::ConfigRequirementsWithSources; -use codex_config::ThreadConfigContext; -use codex_config::ThreadConfigLoader; -use codex_config::config_toml::ConfigToml; -use codex_config::config_toml::ProjectConfig; use codex_exec_server::ExecutorFileSystem; use codex_git_utils::resolve_root_git_project_for_trust; use codex_protocol::config_types::ApprovalsReviewer; @@ -29,71 +41,14 @@ use std::path::Path; use std::path::PathBuf; use toml::Value as TomlValue; -pub use codex_config::AppRequirementToml; -pub use codex_config::AppsRequirementsToml; -pub use codex_config::CloudRequirementsLoadError; -pub use codex_config::CloudRequirementsLoadErrorCode; -pub use codex_config::CloudRequirementsLoader; -pub use codex_config::ConfigError; -pub use codex_config::ConfigLayerEntry; -pub use codex_config::ConfigLayerStack; -pub use codex_config::ConfigLayerStackOrdering; -pub use codex_config::ConfigLoadError; -pub use codex_config::ConfigRequirements; -pub use codex_config::ConfigRequirementsToml; -pub use codex_config::ConstrainedWithSource; -pub use codex_config::FeatureRequirementsToml; -pub use codex_config::FilesystemConstraints; -pub use codex_config::FilesystemDenyReadPattern; -pub use codex_config::HookEventsToml; -pub use codex_config::HookHandlerConfig; -pub use codex_config::LoaderOverrides; -pub use codex_config::ManagedHooksRequirementsToml; -pub use codex_config::MatcherGroup; -pub use codex_config::McpServerIdentity; -pub use codex_config::McpServerRequirement; -pub use codex_config::NetworkConstraints; -pub use codex_config::NetworkDomainPermissionToml; -pub use codex_config::NetworkDomainPermissionsToml; -pub use codex_config::NetworkRequirementsToml; -pub use codex_config::NetworkUnixSocketPermissionToml; -pub use codex_config::NetworkUnixSocketPermissionsToml; -pub use codex_config::RemoteSandboxConfigToml; -pub use codex_config::RequirementSource; -pub use codex_config::ResidencyRequirement; -pub use codex_config::SandboxModeRequirement; -pub use codex_config::Sourced; -pub use codex_config::TextPosition; -pub use codex_config::TextRange; -pub use codex_config::WebSearchModeRequirement; -pub(crate) use codex_config::build_cli_overrides_layer; -pub(crate) use codex_config::config_error_from_toml; -pub use codex_config::default_project_root_markers; -pub use codex_config::format_config_error; -pub use codex_config::format_config_error_with_source; -pub(crate) use codex_config::io_error_from_config_error; -pub use codex_config::merge_toml_values; -pub use codex_config::project_root_markers_from_config; -#[cfg(test)] -pub(crate) use codex_config::version_for_toml; - -/// On Unix systems, load default settings from this file path, if present. -/// Note that /etc/codex/ is treated as a "config folder," so subfolders such -/// as skills/ and rules/ will also be honored. -pub const SYSTEM_CONFIG_TOML_FILE_UNIX: &str = "/etc/codex/config.toml"; +#[cfg(unix)] +const SYSTEM_CONFIG_TOML_FILE_UNIX: &str = "/etc/codex/config.toml"; #[cfg(windows)] const DEFAULT_PROGRAM_DATA_DIR_WINDOWS: &str = r"C:\ProgramData"; -pub(crate) async fn first_layer_config_error(layers: &ConfigLayerStack) -> Option { - codex_config::first_layer_config_error::(layers, CONFIG_TOML_FILE).await -} - -pub(crate) async fn first_layer_config_error_from_entries( - layers: &[ConfigLayerEntry], -) -> Option { - codex_config::first_layer_config_error_from_entries::(layers, CONFIG_TOML_FILE) - .await +async fn first_layer_config_error_from_entries(layers: &[ConfigLayerEntry]) -> Option { + typed_first_layer_config_error_from_entries::(layers, CONFIG_TOML_FILE).await } /// To build up the set of admin-enforced constraints, we build up from multiple @@ -163,7 +118,7 @@ pub async fn load_config_layers_state( .await?; // Honor the system requirements.toml location. - let requirements_toml_file = system_requirements_toml_file()?; + let requirements_toml_file = system_requirements_toml_file_with_overrides(&overrides)?; load_requirements_toml( fs, &mut config_requirements_toml, @@ -175,7 +130,7 @@ pub async fn load_config_layers_state( // Make a best-effort to support the legacy `managed_config.toml` as a // requirements specification. let loaded_config_layers = - layer_io::load_config_layers_internal(fs, codex_home, overrides).await?; + layer_io::load_config_layers_internal(fs, codex_home, overrides.clone()).await?; load_requirements_from_legacy_scheme( &mut config_requirements_toml, loaded_config_layers.clone(), @@ -210,7 +165,7 @@ pub async fn load_config_layers_state( // Include an entry for the "system" config folder, loading its config.toml, // if it exists. - let system_config_toml_file = system_config_toml_file()?; + let system_config_toml_file = system_config_toml_file_with_overrides(&overrides)?; let system_layer = load_config_toml_for_required_layer(fs, &system_config_toml_file, |config_toml| { ConfigLayerEntry::new( @@ -428,7 +383,8 @@ async fn load_config_toml_for_required_layer( /// If available, apply requirements from the platform system /// `requirements.toml` location to `config_requirements_toml` by filling in /// any unset fields. -async fn load_requirements_toml( +#[doc(hidden)] +pub async fn load_requirements_toml( fs: &dyn ExecutorFileSystem, config_requirements_toml: &mut ConfigRequirementsWithSources, requirements_toml_file: &AbsolutePathBuf, @@ -494,16 +450,34 @@ fn system_requirements_toml_file() -> io::Result { windows_system_requirements_toml_file() } +fn system_requirements_toml_file_with_overrides( + overrides: &LoaderOverrides, +) -> io::Result { + match &overrides.system_requirements_path { + Some(path) => AbsolutePathBuf::from_absolute_path(path), + None => system_requirements_toml_file(), + } +} + #[cfg(unix)] -fn system_config_toml_file() -> io::Result { +pub fn system_config_toml_file() -> io::Result { AbsolutePathBuf::from_absolute_path(Path::new(SYSTEM_CONFIG_TOML_FILE_UNIX)) } #[cfg(windows)] -fn system_config_toml_file() -> io::Result { +pub fn system_config_toml_file() -> io::Result { windows_system_config_toml_file() } +fn system_config_toml_file_with_overrides( + overrides: &LoaderOverrides, +) -> io::Result { + match &overrides.system_config_path { + Some(path) => AbsolutePathBuf::from_absolute_path(path), + None => system_config_toml_file(), + } +} + #[cfg(windows)] fn windows_codex_system_dir() -> PathBuf { let program_data = windows_program_data_dir_from_known_folder().unwrap_or_else(|err| { @@ -844,7 +818,8 @@ fn project_trust_for_lookup_key( /// /// This ensures that multiple config layers can be merged together correctly /// even if they were loaded from different directories. -pub(crate) fn resolve_relative_paths_in_config_toml( +#[doc(hidden)] +pub fn resolve_relative_paths_in_config_toml( value_from_config_toml: TomlValue, base_dir: &Path, ) -> io::Result { diff --git a/codex-rs/config/src/state.rs b/codex-rs/config/src/state.rs index 92f36509f..6bb846edd 100644 --- a/codex-rs/config/src/state.rs +++ b/codex-rs/config/src/state.rs @@ -18,6 +18,8 @@ use toml::Value as TomlValue; #[derive(Debug, Default, Clone)] pub struct LoaderOverrides { pub managed_config_path: Option, + pub system_config_path: Option, + pub system_requirements_path: Option, pub ignore_user_config: bool, pub ignore_user_and_project_exec_policy_rules: bool, //TODO(gt): Add a macos_ prefix to this field and remove the target_os check. @@ -31,11 +33,17 @@ impl LoaderOverrides { /// /// This is intended for tests that should load only repo-controlled config fixtures. pub fn without_managed_config_for_tests() -> Self { - Self::with_managed_config_path_for_tests( - std::env::temp_dir() - .join("codex-config-tests") - .join("managed_config.toml"), - ) + let base = std::env::temp_dir().join("codex-config-tests"); + Self { + managed_config_path: Some(base.join("managed_config.toml")), + system_config_path: Some(base.join("config.toml")), + system_requirements_path: Some(base.join("requirements.toml")), + ignore_user_config: false, + ignore_user_and_project_exec_policy_rules: false, + #[cfg(target_os = "macos")] + managed_preferences_base64: Some(String::new()), + macos_managed_config_requirements_base64: Some(String::new()), + } } /// Returns overrides with host MDM disabled and managed config loaded from `managed_config_path`. @@ -44,11 +52,7 @@ impl LoaderOverrides { pub fn with_managed_config_path_for_tests(managed_config_path: PathBuf) -> Self { Self { managed_config_path: Some(managed_config_path), - ignore_user_config: false, - ignore_user_and_project_exec_policy_rules: false, - #[cfg(target_os = "macos")] - managed_preferences_base64: Some(String::new()), - macos_managed_config_requirements_base64: Some(String::new()), + ..Self::without_managed_config_for_tests() } } } diff --git a/codex-rs/config/src/types.rs b/codex-rs/config/src/types.rs index 6668e2531..114ded97e 100644 --- a/codex-rs/config/src/types.rs +++ b/codex-rs/config/src/types.rs @@ -12,15 +12,17 @@ pub use crate::mcp_types::McpServerTransportConfig; pub use crate::mcp_types::RawMcpServerConfig; pub use codex_protocol::config_types::AltScreenMode; pub use codex_protocol::config_types::ApprovalsReviewer; +use codex_protocol::config_types::EnvironmentVariablePattern; pub use codex_protocol::config_types::ModeKind; pub use codex_protocol::config_types::Personality; pub use codex_protocol::config_types::ServiceTier; +use codex_protocol::config_types::ShellEnvironmentPolicy; +use codex_protocol::config_types::ShellEnvironmentPolicyInherit; pub use codex_protocol::config_types::WebSearchMode; use codex_utils_absolute_path::AbsolutePathBuf; use std::collections::BTreeMap; use std::collections::HashMap; use std::fmt; -use wildmatch::WildMatchPattern; use schemars::JsonSchema; use serde::Deserialize; @@ -707,21 +709,6 @@ impl From for codex_app_server_protocol::SandboxSettings } } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(rename_all = "kebab-case")] -pub enum ShellEnvironmentPolicyInherit { - /// "Core" environment variables for the platform. On UNIX, this would - /// include HOME, LOGNAME, PATH, SHELL, and USER, among others. - Core, - - /// Inherits the full environment from the parent process. - #[default] - All, - - /// Do not inherit any environment variables from the parent process. - None, -} - /// Policy for building the `env` when spawning a process via either the /// `shell` or `local_shell` tool. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)] @@ -742,37 +729,6 @@ pub struct ShellEnvironmentPolicyToml { pub experimental_use_profile: Option, } -pub type EnvironmentVariablePattern = WildMatchPattern<'*', '?'>; - -/// Deriving the `env` based on this policy works as follows: -/// 1. Create an initial map based on the `inherit` policy. -/// 2. If `ignore_default_excludes` is false, filter the map using the default -/// exclude pattern(s), which are: `"*KEY*"`, `"*SECRET*"`, and `"*TOKEN*"`. -/// 3. If `exclude` is not empty, filter the map using the provided patterns. -/// 4. Insert any entries from `r#set` into the map. -/// 5. If non-empty, filter the map using the `include_only` patterns. -#[derive(Debug, Clone, PartialEq)] -pub struct ShellEnvironmentPolicy { - /// Starting point when building the environment. - pub inherit: ShellEnvironmentPolicyInherit, - - /// True to skip the check to exclude default environment variables that - /// contain "KEY", "SECRET", or "TOKEN" in their name. Defaults to true. - pub ignore_default_excludes: bool, - - /// Environment variable names to exclude from the environment. - pub exclude: Vec, - - /// (key, value) pairs to insert in the environment. - pub r#set: HashMap, - - /// Environment variable names to retain in the environment. - pub include_only: Vec, - - /// If true, the shell profile will be used to run the command. - pub use_profile: bool, -} - impl From for ShellEnvironmentPolicy { fn from(toml: ShellEnvironmentPolicyToml) -> Self { // Default to inheriting the full environment when not specified. @@ -804,19 +760,6 @@ impl From for ShellEnvironmentPolicy { } } -impl Default for ShellEnvironmentPolicy { - fn default() -> Self { - Self { - inherit: ShellEnvironmentPolicyInherit::All, - ignore_default_excludes: true, - exclude: Vec::new(), - r#set: HashMap::new(), - include_only: Vec::new(), - use_profile: false, - } - } -} - #[cfg(test)] #[path = "types_tests.rs"] mod tests; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 42deea968..b8d3b146f 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -120,9 +120,6 @@ uuid = { workspace = true, features = ["serde", "v4", "v5"] } which = { workspace = true } whoami = { workspace = true } -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9" - # Build OpenSSL from source for musl builds. [target.x86_64-unknown-linux-musl.dependencies] openssl-sys = { workspace = true, features = ["vendored"] } @@ -131,13 +128,6 @@ openssl-sys = { workspace = true, features = ["vendored"] } [target.aarch64-unknown-linux-musl.dependencies] openssl-sys = { workspace = true, features = ["vendored"] } -[target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { version = "0.52", features = [ - "Win32_Foundation", - "Win32_System_Com", - "Win32_UI_Shell", -] } - [target.'cfg(unix)'.dependencies] codex-shell-escalation = { workspace = true } diff --git a/codex-rs/core/src/agent/role.rs b/codex-rs/core/src/agent/role.rs index 0ee1de760..2ab16cd22 100644 --- a/codex-rs/core/src/agent/role.rs +++ b/codex-rs/core/src/agent/role.rs @@ -11,13 +11,13 @@ use crate::config::Config; use crate::config::ConfigOverrides; use crate::config::agent_roles::parse_agent_role_file_contents; use crate::config::deserialize_config_toml_with_base; -use crate::config_loader::ConfigLayerEntry; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::resolve_relative_paths_in_config_toml; use anyhow::anyhow; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; use codex_config::config_toml::ConfigToml; +use codex_config::loader::resolve_relative_paths_in_config_toml; use codex_exec_server::LOCAL_FS; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/codex-rs/core/src/agent/role_tests.rs b/codex-rs/core/src/agent/role_tests.rs index f379fbef1..d8b277db9 100644 --- a/codex-rs/core/src/agent/role_tests.rs +++ b/codex-rs/core/src/agent/role_tests.rs @@ -2,9 +2,9 @@ use super::*; use crate::SkillsManager; use crate::config::CONFIG_TOML_FILE; use crate::config::ConfigBuilder; -use crate::config_loader::ConfigLayerStackOrdering; use crate::plugins::PluginsManager; use crate::skills_load_input_from_config; +use codex_config::ConfigLayerStackOrdering; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::Verbosity; use codex_protocol::openai_models::ReasoningEffort; diff --git a/codex-rs/core/src/agents_md.rs b/codex-rs/core/src/agents_md.rs index b7fb7b11c..7a9fd7493 100644 --- a/codex-rs/core/src/agents_md.rs +++ b/codex-rs/core/src/agents_md.rs @@ -16,11 +16,11 @@ //! 3. We do **not** walk past the project root. use crate::config::Config; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::default_project_root_markers; -use crate::config_loader::merge_toml_values; -use crate::config_loader::project_root_markers_from_config; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::ConfigLayerStackOrdering; +use codex_config::default_project_root_markers; +use codex_config::merge_toml_values; +use codex_config::project_root_markers_from_config; use codex_exec_server::Environment; use codex_exec_server::ExecutorFileSystem; use codex_features::Feature; diff --git a/codex-rs/core/src/config/agent_roles.rs b/codex-rs/core/src/config/agent_roles.rs index 898ddef8c..abdef33e7 100644 --- a/codex-rs/core/src/config/agent_roles.rs +++ b/codex-rs/core/src/config/agent_roles.rs @@ -1,6 +1,6 @@ use super::AgentRoleConfig; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; use codex_config::config_toml::AgentRoleToml; use codex_config::config_toml::AgentsToml; use codex_config::config_toml::ConfigToml; diff --git a/codex-rs/core/src/config_loader/tests.rs b/codex-rs/core/src/config/config_loader_tests.rs similarity index 95% rename from codex-rs/core/src/config_loader/tests.rs rename to codex-rs/core/src/config/config_loader_tests.rs index 82d621a5f..00d67ae1e 100644 --- a/codex-rs/core/src/config_loader/tests.rs +++ b/codex-rs/core/src/config/config_loader_tests.rs @@ -1,25 +1,29 @@ -use super::LoaderOverrides; -use super::load_config_layers_state; use crate::config::ConfigBuilder; use crate::config::ConfigOverrides; use crate::config::ConstraintError; -use crate::config_loader::CloudRequirementsLoadError; -use crate::config_loader::CloudRequirementsLoader; -use crate::config_loader::ConfigLayerEntry; -use crate::config_loader::ConfigLoadError; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; -use crate::config_loader::ConfigRequirementsWithSources; -use crate::config_loader::FilesystemDenyReadPattern; -use crate::config_loader::RequirementSource; -use crate::config_loader::load_requirements_toml; -use crate::config_loader::version_for_toml; +use codex_app_server_protocol::ConfigLayerSource; use codex_config::CONFIG_TOML_FILE; +use codex_config::CloudRequirementsLoadError; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigError; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStackOrdering; +use codex_config::ConfigLoadError; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; +use codex_config::ConfigRequirementsWithSources; +use codex_config::FilesystemDenyReadPattern; +use codex_config::LoaderOverrides; +use codex_config::RequirementSource; use codex_config::SessionThreadConfig; use codex_config::StaticThreadConfigLoader; use codex_config::ThreadConfigSource; +use codex_config::config_error_from_toml; use codex_config::config_toml::ConfigToml; use codex_config::config_toml::ProjectConfig; +use codex_config::loader::load_config_layers_state; +use codex_config::loader::load_requirements_toml; +use codex_config::version_for_toml; use codex_exec_server::LOCAL_FS; use codex_protocol::config_types::TrustLevel; use codex_protocol::config_types::WebSearchMode; @@ -33,7 +37,7 @@ use std::path::Path; use tempfile::tempdir; use toml::Value as TomlValue; -fn config_error_from_io(err: &std::io::Error) -> &super::ConfigError { +fn config_error_from_io(err: &std::io::Error) -> &ConfigError { err.get_ref() .and_then(|err| err.downcast_ref::()) .map(ConfigLoadError::config_error) @@ -110,8 +114,7 @@ async fn returns_config_error_for_invalid_user_config_toml() { let config_error = config_error_from_io(&err); let expected_toml_error = toml::from_str::(contents).expect_err("parse error"); - let expected_config_error = - super::config_error_from_toml(&config_path, contents, expected_toml_error); + let expected_config_error = config_error_from_toml(&config_path, contents, expected_toml_error); assert_eq!(config_error, &expected_config_error); } @@ -202,7 +205,7 @@ async fn returns_config_error_for_invalid_managed_config_toml() { let config_error = config_error_from_io(&err); let expected_toml_error = toml::from_str::(contents).expect_err("parse error"); let expected_config_error = - super::config_error_from_toml(&managed_path, contents, expected_toml_error); + config_error_from_toml(&managed_path, contents, expected_toml_error); assert_eq!(config_error, &expected_config_error); } @@ -325,7 +328,7 @@ async fn returns_empty_when_all_layers_missing() { .expect("expected a user layer even when CODEX_HOME/config.toml does not exist"); assert_eq!( &ConfigLayerEntry { - name: super::ConfigLayerSource::User { + name: ConfigLayerSource::User { file: AbsolutePathBuf::resolve_path_against_base(CONFIG_TOML_FILE, tmp.path()) }, config: TomlValue::Table(toml::map::Map::new()), @@ -350,7 +353,7 @@ async fn returns_empty_when_all_layers_missing() { let num_system_layers = layers .layers_high_to_low() .iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::System { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::System { .. })) .count(); assert_eq!( num_system_layers, 1, @@ -374,12 +377,19 @@ async fn includes_thread_config_layers_in_stack() -> anyhow::Result<()> { let cwd_dir = tmp.path().join("project"); tokio::fs::create_dir_all(&cwd_dir).await?; let cwd = AbsolutePathBuf::from_absolute_path(&cwd_dir)?; + let overrides = LoaderOverrides::without_managed_config_for_tests(); + let expected_system_config = AbsolutePathBuf::from_absolute_path( + overrides + .system_config_path + .as_ref() + .expect("test overrides should include a system config path"), + )?; let layers = load_config_layers_state( LOCAL_FS.as_ref(), tmp.path(), Some(cwd), &[("features.plugins".to_string(), TomlValue::Boolean(true))], - LoaderOverrides::without_managed_config_for_tests(), + overrides, CloudRequirementsLoader::default(), &StaticThreadConfigLoader::new(vec![ThreadConfigSource::Session(SessionThreadConfig { features: BTreeMap::from([("plugins".to_string(), false)]), @@ -397,13 +407,13 @@ async fn includes_thread_config_layers_in_stack() -> anyhow::Result<()> { assert_eq!( layer_sources, vec![ - super::ConfigLayerSource::SessionFlags, - super::ConfigLayerSource::SessionFlags, - super::ConfigLayerSource::User { + ConfigLayerSource::SessionFlags, + ConfigLayerSource::SessionFlags, + ConfigLayerSource::User { file: AbsolutePathBuf::resolve_path_against_base(CONFIG_TOML_FILE, tmp.path()), }, - super::ConfigLayerSource::System { - file: super::system_config_toml_file()?, + ConfigLayerSource::System { + file: expected_system_config, }, ] ); @@ -482,7 +492,7 @@ flag = false .find(|layer| { matches!( layer.name, - super::ConfigLayerSource::LegacyManagedConfigTomlFromMdm + ConfigLayerSource::LegacyManagedConfigTomlFromMdm ) }) .expect("mdm layer"); @@ -687,14 +697,14 @@ personality = true .allowed_web_search_modes .as_deref() .cloned(), - Some(vec![crate::config_loader::WebSearchModeRequirement::Cached]) + Some(vec![codex_config::WebSearchModeRequirement::Cached]) ); assert_eq!( config_requirements_toml .feature_requirements .as_ref() .map(|requirements| requirements.value.clone()), - Some(crate::config_loader::FeatureRequirementsToml { + Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([("personality".to_string(), true)]), }) ); @@ -733,14 +743,14 @@ personality = true ); assert_eq!( config_requirements.enforce_residency.value(), - Some(crate::config_loader::ResidencyRequirement::Us) + Some(codex_config::ResidencyRequirement::Us) ); assert_eq!( config_requirements .feature_requirements .as_ref() .map(|requirements| requirements.value.clone()), - Some(crate::config_loader::FeatureRequirementsToml { + Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([("personality".to_string(), true)]), }) ); @@ -1174,8 +1184,8 @@ async fn load_config_layers_applies_matching_remote_sandbox_config() -> anyhow:: assert_eq!( layers.requirements_toml().allowed_sandbox_modes, Some(vec![ - crate::config_loader::SandboxModeRequirement::ReadOnly, - crate::config_loader::SandboxModeRequirement::WorkspaceWrite, + codex_config::SandboxModeRequirement::ReadOnly, + codex_config::SandboxModeRequirement::WorkspaceWrite, ]) ); assert!( @@ -1267,7 +1277,7 @@ async fn project_layers_prefer_closest_cwd() -> std::io::Result<()> { .layers_high_to_low() .into_iter() .filter_map(|layer| match &layer.name { - super::ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), + ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), _ => None, }) .collect(); @@ -1413,11 +1423,11 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s let project_layers: Vec<_> = layers .layers_high_to_low() .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!( vec![&ConfigLayerEntry { - name: super::ConfigLayerSource::Project { + name: ConfigLayerSource::Project { dot_codex_folder: AbsolutePathBuf::from_absolute_path(project_root.join(".codex"))?, }, config: TomlValue::Table(toml::map::Map::new()), @@ -1454,11 +1464,11 @@ async fn codex_home_is_not_loaded_as_project_layer_from_home_dir() -> std::io::R let project_layers: Vec<_> = layers .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); let expected: Vec<&ConfigLayerEntry> = Vec::new(); assert_eq!(expected, project_layers); @@ -1513,17 +1523,17 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul let project_layers: Vec<_> = layers .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); let child_config: TomlValue = toml::from_str("foo = \"child\"\n").expect("parse child config"); assert_eq!( vec![&ConfigLayerEntry { - name: super::ConfigLayerSource::Project { + name: ConfigLayerSource::Project { dot_codex_folder: AbsolutePathBuf::from_absolute_path(&nested_dot_codex)?, }, config: child_config.clone(), @@ -1585,11 +1595,11 @@ async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result< .await?; let project_layers_untrusted: Vec<_> = layers_untrusted .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers_untrusted.len(), 1); assert!( @@ -1626,11 +1636,11 @@ async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result< .await?; let project_layers_unknown: Vec<_> = layers_unknown .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers_unknown.len(), 1); assert!( @@ -1695,11 +1705,11 @@ async fn project_trust_does_not_match_configured_alias_for_canonical_cwd() -> st let project_layers: Vec<_> = layers .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!(project_layers.len(), 1); assert!( @@ -1849,11 +1859,11 @@ async fn invalid_project_config_ignored_when_untrusted_or_unknown() -> std::io:: .await?; let project_layers: Vec<_> = layers .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!( project_layers.len(), @@ -1919,11 +1929,11 @@ async fn project_layer_without_config_toml_is_disabled_when_untrusted_or_unknown .await?; let project_layers: Vec<_> = layers .get_layers( - super::ConfigLayerStackOrdering::HighestPrecedenceFirst, + ConfigLayerStackOrdering::HighestPrecedenceFirst, /*include_disabled*/ true, ) .into_iter() - .filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. })) + .filter(|layer| matches!(layer.name, ConfigLayerSource::Project { .. })) .collect(); assert_eq!( project_layers.len(), @@ -2029,7 +2039,7 @@ async fn project_root_markers_supports_alternate_markers() -> std::io::Result<() .layers_high_to_low() .into_iter() .filter_map(|layer| match &layer.name { - super::ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), + ConfigLayerSource::Project { dot_codex_folder } => Some(dot_codex_folder), _ => None, }) .collect(); @@ -2051,14 +2061,14 @@ async fn project_root_markers_supports_alternate_markers() -> std::io::Result<() } mod requirements_exec_policy_tests { - use crate::config_loader::ConfigLayerEntry; - use crate::config_loader::ConfigLayerStack; - use crate::config_loader::ConfigRequirements; - use crate::config_loader::ConfigRequirementsToml; - use crate::config_loader::ConfigRequirementsWithSources; - use crate::config_loader::RequirementSource; use crate::exec_policy::load_exec_policy; use codex_app_server_protocol::ConfigLayerSource; + use codex_config::ConfigLayerEntry; + use codex_config::ConfigLayerStack; + use codex_config::ConfigRequirements; + use codex_config::ConfigRequirementsToml; + use codex_config::ConfigRequirementsWithSources; + use codex_config::RequirementSource; use codex_config::RequirementsExecPolicyDecisionToml; use codex_config::RequirementsExecPolicyParseError; use codex_config::RequirementsExecPolicyPatternTokenToml; diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 3b0dd3359..38dce5df3 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -4,11 +4,10 @@ use crate::config::ThreadStoreConfig; use crate::config::edit::ConfigEdit; use crate::config::edit::ConfigEditsBuilder; use crate::config::edit::apply_blocking; -use crate::config_loader::RequirementSource; -use crate::config_loader::project_trust_key; use crate::plugins::PluginsManager; use assert_matches::assert_matches; use codex_config::CONFIG_TOML_FILE; +use codex_config::RequirementSource; use codex_config::config_toml::AgentRoleToml; use codex_config::config_toml::AgentsToml; use codex_config::config_toml::AutoReviewToml; @@ -21,6 +20,7 @@ use codex_config::config_toml::RealtimeTransport; use codex_config::config_toml::RealtimeWsMode; use codex_config::config_toml::RealtimeWsVersion; use codex_config::config_toml::ToolsToml; +use codex_config::loader::project_trust_key; use codex_config::permissions_toml::FilesystemPermissionToml; use codex_config::permissions_toml::FilesystemPermissionsToml; use codex_config::permissions_toml::NetworkDomainPermissionToml; @@ -3981,7 +3981,7 @@ async fn load_config_uses_requirements_guardian_policy_config() -> std::io::Resu let config_layer_stack = ConfigLayerStack::new( Vec::new(), Default::default(), - crate::config_loader::ConfigRequirementsToml { + codex_config::ConfigRequirementsToml { guardian_policy_config: Some( " Use the workspace-managed guardian policy. ".to_string(), ), @@ -4062,7 +4062,7 @@ async fn requirements_guardian_policy_beats_auto_review() -> std::io::Result<()> let config_layer_stack = ConfigLayerStack::new( Vec::new(), Default::default(), - crate::config_loader::ConfigRequirementsToml { + codex_config::ConfigRequirementsToml { guardian_policy_config: Some("Use the managed guardian policy.".to_string()), ..Default::default() }, @@ -4126,7 +4126,7 @@ async fn load_config_ignores_empty_requirements_guardian_policy_config() -> std: let config_layer_stack = ConfigLayerStack::new( Vec::new(), Default::default(), - crate::config_loader::ConfigRequirementsToml { + codex_config::ConfigRequirementsToml { guardian_policy_config: Some(" ".to_string()), ..Default::default() }, @@ -4258,15 +4258,15 @@ config_file = "./agents/researcher.toml" "#, ) .expect("agent role layer config should parse"); - let config_layer_stack = crate::config_loader::ConfigLayerStack::new( - vec![crate::config_loader::ConfigLayerEntry::new( + let config_layer_stack = codex_config::ConfigLayerStack::new( + vec![codex_config::ConfigLayerEntry::new( codex_app_server_protocol::ConfigLayerSource::User { file: codex_home.path().join(CONFIG_TOML_FILE).abs(), }, layer_config, )], Default::default(), - crate::config_loader::ConfigRequirementsToml::default(), + codex_config::ConfigRequirementsToml::default(), ) .map_err(std::io::Error::other)?; @@ -6035,14 +6035,12 @@ async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset() { let fixture = create_test_fixture()?; - let requirements_toml = crate::config_loader::ConfigRequirementsToml { + let requirements_toml = codex_config::ConfigRequirementsToml { allowed_approval_policies: None, allowed_approvals_reviewers: None, allowed_sandbox_modes: None, remote_sandbox_config: None, - allowed_web_search_modes: Some(vec![ - crate::config_loader::WebSearchModeRequirement::Cached, - ]), + allowed_web_search_modes: Some(vec![codex_config::WebSearchModeRequirement::Cached]), feature_requirements: None, hooks: None, mcp_servers: None, @@ -6053,7 +6051,7 @@ async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset() permissions: None, guardian_policy_config: None, }; - let requirement_source = crate::config_loader::RequirementSource::Unknown; + let requirement_source = codex_config::RequirementSource::Unknown; let requirement_source_for_error = requirement_source.clone(); let allowed = vec![WebSearchMode::Disabled, WebSearchMode::Cached]; let constrained = Constrained::new(WebSearchMode::Cached, move |candidate| { @@ -6068,15 +6066,15 @@ async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset() }) } })?; - let requirements = crate::config_loader::ConfigRequirements { - web_search_mode: crate::config_loader::ConstrainedWithSource::new( + let requirements = codex_config::ConfigRequirements { + web_search_mode: codex_config::ConstrainedWithSource::new( constrained, Some(requirement_source), ), ..Default::default() }; let config_layer_stack = - crate::config_loader::ConfigLayerStack::new(Vec::new(), requirements, requirements_toml) + codex_config::ConfigLayerStack::new(Vec::new(), requirements, requirements_toml) .expect("config layer stack"); let config = Config::load_config_with_layer_stack( @@ -6688,10 +6686,8 @@ async fn requirements_disallowing_default_sandbox_falls_back_to_required_default let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - allowed_sandbox_modes: Some(vec![ - crate::config_loader::SandboxModeRequirement::ReadOnly, - ]), + Ok(Some(codex_config::ConfigRequirementsToml { + allowed_sandbox_modes: Some(vec![codex_config::SandboxModeRequirement::ReadOnly]), ..Default::default() })) })) @@ -6713,10 +6709,10 @@ async fn explicit_sandbox_mode_falls_back_when_disallowed_by_requirements() -> s "#, )?; - let requirements = crate::config_loader::ConfigRequirementsToml { + let requirements = codex_config::ConfigRequirementsToml { allowed_approval_policies: None, allowed_approvals_reviewers: None, - allowed_sandbox_modes: Some(vec![crate::config_loader::SandboxModeRequirement::ReadOnly]), + allowed_sandbox_modes: Some(vec![codex_config::SandboxModeRequirement::ReadOnly]), remote_sandbox_config: None, allowed_web_search_modes: None, feature_requirements: None, @@ -6793,9 +6789,9 @@ async fn requirements_web_search_mode_overrides_danger_full_access_default() -> .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_web_search_modes: Some(vec![ - crate::config_loader::WebSearchModeRequirement::Cached, + codex_config::WebSearchModeRequirement::Cached, ]), ..Default::default() })) @@ -6834,7 +6830,7 @@ trust_level = "untrusted" .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(workspace.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approval_policies: Some(vec![AskForApproval::OnRequest]), ..Default::default() })) @@ -6863,7 +6859,7 @@ async fn explicit_approval_policy_falls_back_when_disallowed_by_requirements() - .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approval_policies: Some(vec![AskForApproval::OnRequest]), ..Default::default() })) @@ -6884,8 +6880,8 @@ async fn feature_requirements_normalize_effective_feature_values() -> std::io::R let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([ ("personality".to_string(), true), ("shell_tool".to_string(), false), @@ -6918,8 +6914,8 @@ async fn feature_requirements_auto_review_disables_guardian_approval() -> std::i let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([("auto_review".to_string(), false)]), }), ..Default::default() @@ -6940,8 +6936,8 @@ async fn browser_feature_requirements_are_valid() -> std::io::Result<()> { let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([ ("in_app_browser".to_string(), false), ("browser_use".to_string(), false), @@ -6975,8 +6971,8 @@ shell_tool = true .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([ ("personality".to_string(), true), ("shell_tool".to_string(), false), @@ -7122,7 +7118,7 @@ async fn requirements_disallowing_default_approvals_reviewer_falls_back_to_requi let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approvals_reviewers: Some(vec![ApprovalsReviewer::AutoReview]), ..Default::default() })) @@ -7148,7 +7144,7 @@ async fn root_approvals_reviewer_falls_back_when_disallowed_by_requirements() -> .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approvals_reviewers: Some(vec![ApprovalsReviewer::AutoReview]), ..Default::default() })) @@ -7185,7 +7181,7 @@ approvals_reviewer = "user" .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approvals_reviewers: Some(vec![ApprovalsReviewer::AutoReview]), ..Default::default() })) @@ -7211,7 +7207,7 @@ async fn approvals_reviewer_preserves_valid_user_choice_when_allowed_by_requirem .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { allowed_approvals_reviewers: Some(vec![ ApprovalsReviewer::User, ApprovalsReviewer::AutoReview, @@ -7363,8 +7359,8 @@ async fn feature_requirements_normalize_runtime_feature_mutations() -> std::io:: let mut config = ConfigBuilder::default() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([ ("personality".to_string(), true), ("shell_tool".to_string(), false), @@ -7399,8 +7395,8 @@ async fn feature_requirements_warn_on_collab_legacy_alias() -> std::io::Result<( let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([("collab".to_string(), true)]), }), ..Default::default() @@ -7429,8 +7425,8 @@ async fn feature_requirements_warn_and_ignore_unknown_feature() -> std::io::Resu let config = ConfigBuilder::without_managed_config_for_tests() .codex_home(codex_home.path().to_path_buf()) .cloud_requirements(CloudRequirementsLoader::new(async { - Ok(Some(crate::config_loader::ConfigRequirementsToml { - feature_requirements: Some(crate::config_loader::FeatureRequirementsToml { + Ok(Some(codex_config::ConfigRequirementsToml { + feature_requirements: Some(codex_config::FeatureRequirementsToml { entries: BTreeMap::from([("made_up_feature".to_string(), true)]), }), ..Default::default() diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index c7f13c63d..e8c83fe95 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1,20 +1,6 @@ use crate::agents_md::AgentsMdManager; use crate::config::edit::ConfigEdit; use crate::config::edit::ConfigEditsBuilder; -use crate::config_loader::CloudRequirementsLoader; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; -use crate::config_loader::ConstrainedWithSource; -use crate::config_loader::FeatureRequirementsToml; -use crate::config_loader::LoaderOverrides; -use crate::config_loader::McpServerIdentity; -use crate::config_loader::McpServerRequirement; -use crate::config_loader::ResidencyRequirement; -use crate::config_loader::Sourced; -use crate::config_loader::load_config_layers_state; -use crate::config_loader::project_trust_key; use crate::memories::memory_root; use crate::path_utils::normalize_for_native_workdir; use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS; @@ -22,6 +8,18 @@ use crate::unified_exec::MIN_EMPTY_YIELD_TIME_MS; use crate::windows_sandbox::WindowsSandboxLevelExt; use crate::windows_sandbox::resolve_windows_sandbox_mode; use crate::windows_sandbox::resolve_windows_sandbox_private_desktop; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; +use codex_config::ConstrainedWithSource; +use codex_config::FeatureRequirementsToml; +use codex_config::LoaderOverrides; +use codex_config::McpServerIdentity; +use codex_config::McpServerRequirement; +use codex_config::ResidencyRequirement; +use codex_config::Sourced; use codex_config::ThreadConfigLoader; use codex_config::config_toml::ConfigToml; use codex_config::config_toml::ProjectConfig; @@ -29,6 +27,8 @@ use codex_config::config_toml::RealtimeAudioConfig; use codex_config::config_toml::RealtimeConfig; use codex_config::config_toml::ThreadStoreToml; use codex_config::config_toml::validate_model_providers; +use codex_config::loader::load_config_layers_state; +use codex_config::loader::project_trust_key; use codex_config::profile_toml::ConfigProfile; use codex_config::types::ApprovalsReviewer; use codex_config::types::AuthCredentialsStoreMode; @@ -44,7 +44,6 @@ use codex_config::types::OAuthCredentialsStoreMode; use codex_config::types::OtelConfig; use codex_config::types::OtelConfigToml; use codex_config::types::OtelExporterKind; -use codex_config::types::ShellEnvironmentPolicy; use codex_config::types::ToolSuggestConfig; use codex_config::types::ToolSuggestDiscoverable; use codex_config::types::TuiNotificationSettings; @@ -74,6 +73,7 @@ use codex_protocol::config_types::Personality; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::SandboxMode; use codex_protocol::config_types::ServiceTier; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::config_types::TrustLevel; use codex_protocol::config_types::Verbosity; use codex_protocol::config_types::WebSearchConfig; @@ -876,10 +876,13 @@ impl ConfigBuilder { let config_toml: ConfigToml = match merged_toml.try_into() { Ok(config_toml) => config_toml, Err(err) => { - if let Some(config_error) = - crate::config_loader::first_layer_config_error(&config_layer_stack).await + if let Some(config_error) = codex_config::first_layer_config_error::( + &config_layer_stack, + codex_config::CONFIG_TOML_FILE, + ) + .await { - return Err(crate::config_loader::io_error_from_config_error( + return Err(codex_config::io_error_from_config_error( std::io::ErrorKind::InvalidData, config_error, Some(err), @@ -979,8 +982,8 @@ impl Config { format!("failed to serialize default config: {e}"), ) })?; - let cli_layer = crate::config_loader::build_cli_overrides_layer(&cli_overrides); - crate::config_loader::merge_toml_values(&mut merged, &cli_layer); + let cli_layer = codex_config::build_cli_overrides_layer(&cli_overrides); + codex_config::merge_toml_values(&mut merged, &cli_layer); let codex_home = AbsolutePathBuf::from_absolute_path_checked(codex_home)?; let config_toml = deserialize_config_toml_with_base(merged, &codex_home)?; Self::load_config_with_layer_stack( @@ -1462,7 +1465,7 @@ fn resolve_permission_config_syntax( fn apply_managed_filesystem_constraints( file_system_sandbox_policy: &mut FileSystemSandboxPolicy, - filesystem_constraints: &crate::config_loader::FilesystemConstraints, + filesystem_constraints: &codex_config::FilesystemConstraints, ) { for deny_read in &filesystem_constraints.deny_read { let deny_entry = if deny_read.contains_glob() { @@ -2801,3 +2804,7 @@ pub fn log_dir(cfg: &Config) -> std::io::Result { #[cfg(test)] #[path = "config_tests.rs"] mod tests; + +#[cfg(test)] +#[path = "config_loader_tests.rs"] +mod config_loader_tests; diff --git a/codex-rs/core/src/config/network_proxy_spec.rs b/codex-rs/core/src/config/network_proxy_spec.rs index acabe24f2..1bb5e1c9f 100644 --- a/codex-rs/core/src/config/network_proxy_spec.rs +++ b/codex-rs/core/src/config/network_proxy_spec.rs @@ -1,5 +1,5 @@ -use crate::config_loader::NetworkConstraints; use async_trait::async_trait; +use codex_config::NetworkConstraints; use codex_execpolicy::Policy; use codex_network_proxy::BlockedRequestObserver; use codex_network_proxy::ConfigReloader; diff --git a/codex-rs/core/src/config/network_proxy_spec_tests.rs b/codex-rs/core/src/config/network_proxy_spec_tests.rs index 5ba4bd153..fb4231aca 100644 --- a/codex-rs/core/src/config/network_proxy_spec_tests.rs +++ b/codex-rs/core/src/config/network_proxy_spec_tests.rs @@ -1,6 +1,6 @@ use super::*; -use crate::config_loader::NetworkDomainPermissionToml; -use crate::config_loader::NetworkDomainPermissionsToml; +use codex_config::NetworkDomainPermissionToml; +use codex_config::NetworkDomainPermissionsToml; use codex_network_proxy::NetworkDomainPermission; use pretty_assertions::assert_eq; diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index 4c710e3a3..456f3a7ea 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -25,11 +25,11 @@ use serde::de::DeserializeOwned; use tracing::warn; use crate::config::Config; -use crate::config_loader::AppsRequirementsToml; use crate::mcp::McpManager; use crate::plugins::PluginsManager; use crate::plugins::list_tool_suggest_discoverable_plugins; use crate::session::INITIAL_SUBMIT_ID; +use codex_config::AppsRequirementsToml; use codex_config::types::AppToolApproval; use codex_config::types::AppsConfigToml; use codex_config::types::ToolSuggestDiscoverableType; diff --git a/codex-rs/core/src/connectors_tests.rs b/codex-rs/core/src/connectors_tests.rs index 0f9e834d8..885b573da 100644 --- a/codex-rs/core/src/connectors_tests.rs +++ b/codex-rs/core/src/connectors_tests.rs @@ -1,12 +1,12 @@ use super::*; use crate::config::CONFIG_TOML_FILE; use crate::config::ConfigBuilder; -use crate::config_loader::AppRequirementToml; -use crate::config_loader::AppsRequirementsToml; -use crate::config_loader::CloudRequirementsLoader; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; +use codex_config::AppRequirementToml; +use codex_config::AppsRequirementsToml; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigLayerStack; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; use codex_config::types::AppConfig; use codex_config::types::AppToolConfig; use codex_config::types::AppToolsConfig; diff --git a/codex-rs/core/src/exec_env.rs b/codex-rs/core/src/exec_env.rs index ad94bc51a..938667b12 100644 --- a/codex-rs/core/src/exec_env.rs +++ b/codex-rs/core/src/exec_env.rs @@ -1,10 +1,11 @@ -#[cfg(test)] -use codex_config::types::EnvironmentVariablePattern; -use codex_config::types::ShellEnvironmentPolicy; use codex_protocol::ThreadId; +#[cfg(test)] +use codex_protocol::config_types::EnvironmentVariablePattern; +use codex_protocol::config_types::ShellEnvironmentPolicy; +use codex_protocol::shell_environment; use std::collections::HashMap; -pub use codex_config::shell_environment::CODEX_THREAD_ID_ENV_VAR; +pub use codex_protocol::shell_environment::CODEX_THREAD_ID_ENV_VAR; /// Construct an environment map based on the rules in the specified policy. The /// resulting map can be passed directly to `Command::envs()` after calling @@ -21,7 +22,7 @@ pub fn create_env( thread_id: Option, ) -> HashMap { let thread_id = thread_id.map(|thread_id| thread_id.to_string()); - codex_config::shell_environment::create_env(policy, thread_id.as_deref()) + shell_environment::create_env(policy, thread_id.as_deref()) } #[cfg(all(test, target_os = "windows"))] @@ -34,7 +35,7 @@ where I: IntoIterator, { let thread_id = thread_id.map(|thread_id| thread_id.to_string()); - codex_config::shell_environment::create_env_from_vars(vars, policy, thread_id.as_deref()) + shell_environment::create_env_from_vars(vars, policy, thread_id.as_deref()) } #[cfg(test)] @@ -47,7 +48,7 @@ where I: IntoIterator, { let thread_id = thread_id.map(|thread_id| thread_id.to_string()); - codex_config::shell_environment::populate_env(vars, policy, thread_id.as_deref()) + shell_environment::populate_env(vars, policy, thread_id.as_deref()) } #[cfg(test)] diff --git a/codex-rs/core/src/exec_env_tests.rs b/codex-rs/core/src/exec_env_tests.rs index 81b5c0bb3..725edd8cc 100644 --- a/codex-rs/core/src/exec_env_tests.rs +++ b/codex-rs/core/src/exec_env_tests.rs @@ -1,5 +1,5 @@ use super::*; -use codex_config::types::ShellEnvironmentPolicyInherit; +use codex_protocol::config_types::ShellEnvironmentPolicyInherit; use maplit::hashmap; use pretty_assertions::assert_eq; diff --git a/codex-rs/core/src/exec_policy.rs b/codex-rs/core/src/exec_policy.rs index 54ad8058d..9fbb5b015 100644 --- a/codex-rs/core/src/exec_policy.rs +++ b/codex-rs/core/src/exec_policy.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use arc_swap::ArcSwap; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; use codex_execpolicy::AmendError; use codex_execpolicy::Decision; use codex_execpolicy::Error as ExecPolicyRuleError; diff --git a/codex-rs/core/src/exec_policy_tests.rs b/codex-rs/core/src/exec_policy_tests.rs index fe4560a78..c1f6aa0e6 100644 --- a/codex-rs/core/src/exec_policy_tests.rs +++ b/codex-rs/core/src/exec_policy_tests.rs @@ -1,17 +1,17 @@ use super::*; use crate::config::Config; use crate::config::ConfigBuilder; -use crate::config_loader::ConfigLayerEntry; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; -use crate::config_loader::LoaderOverrides; -use crate::config_loader::RequirementSource; -use crate::config_loader::Sourced; use codex_app_server_protocol::ConfigLayerSource; use codex_config::CONFIG_TOML_FILE; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; +use codex_config::LoaderOverrides; +use codex_config::RequirementSource; use codex_config::RequirementsExecPolicy; +use codex_config::Sourced; use codex_config::config_toml::ConfigToml; use codex_config::config_toml::ProjectConfig; use codex_protocol::config_types::TrustLevel; diff --git a/codex-rs/core/src/guardian/tests.rs b/codex-rs/core/src/guardian/tests.rs index c78884bce..76b4a8464 100644 --- a/codex-rs/core/src/guardian/tests.rs +++ b/codex-rs/core/src/guardian/tests.rs @@ -5,18 +5,18 @@ use crate::config::Constrained; use crate::config::ManagedFeatures; use crate::config::NetworkProxySpec; use crate::config::test_config; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::FeatureRequirementsToml; -use crate::config_loader::NetworkConstraints; -use crate::config_loader::NetworkDomainPermissionToml; -use crate::config_loader::NetworkDomainPermissionsToml; -use crate::config_loader::RequirementSource; -use crate::config_loader::Sourced; use crate::guardian::approval_request::guardian_request_target_item_id; use crate::session::session::Session; use crate::session::turn_context::TurnContext; use crate::test_support; use codex_analytics::GuardianApprovalRequestSource; +use codex_config::ConfigLayerStack; +use codex_config::FeatureRequirementsToml; +use codex_config::NetworkConstraints; +use codex_config::NetworkDomainPermissionToml; +use codex_config::NetworkDomainPermissionsToml; +use codex_config::RequirementSource; +use codex_config::Sourced; use codex_config::config_toml::ConfigToml; use codex_config::types::McpServerConfig; use codex_exec_server::LOCAL_FS; @@ -2122,7 +2122,7 @@ async fn guardian_review_session_config_uses_requirements_guardian_policy_config let config_layer_stack = ConfigLayerStack::new( Vec::new(), Default::default(), - crate::config_loader::ConfigRequirementsToml { + codex_config::ConfigRequirementsToml { guardian_policy_config: Some( " Use the workspace-managed guardian policy. ".to_string(), ), diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 3e2d2ee52..c6f879209 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -25,7 +25,6 @@ mod codex_delegate; mod command_canonicalization; mod commit_attribution; pub mod config; -pub mod config_loader; pub mod connectors; pub mod context; mod context_manager; diff --git a/codex-rs/core/src/network_proxy_loader.rs b/codex-rs/core/src/network_proxy_loader.rs index 78428fabf..f168b79f4 100644 --- a/codex-rs/core/src/network_proxy_loader.rs +++ b/codex-rs/core/src/network_proxy_loader.rs @@ -1,10 +1,5 @@ use crate::config::find_codex_home; use crate::config::resolve_permission_profile; -use crate::config_loader::CloudRequirementsLoader; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::LoaderOverrides; -use crate::config_loader::load_config_layers_state; use crate::exec_policy::ExecPolicyError; use crate::exec_policy::format_exec_policy_error_with_source; use crate::exec_policy::load_exec_policy; @@ -13,6 +8,11 @@ use anyhow::Result; use async_trait::async_trait; use codex_app_server_protocol::ConfigLayerSource; use codex_config::CONFIG_TOML_FILE; +use codex_config::CloudRequirementsLoader; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::LoaderOverrides; +use codex_config::loader::load_config_layers_state; use codex_config::permissions_toml::NetworkToml; use codex_config::permissions_toml::PermissionsToml; use codex_config::permissions_toml::overlay_network_domain_permissions; diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index 77265ece7..880ad8ed2 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -4,8 +4,8 @@ use crate::SkillMetadata; use crate::config::Config; use crate::config::edit::ConfigEdit; use crate::config::edit::ConfigEditsBuilder; -use crate::config_loader::ConfigLayerStack; use codex_analytics::AnalyticsEventsClient; +use codex_config::ConfigLayerStack; use codex_config::types::PluginConfig; use codex_core_plugins::OPENAI_CURATED_MARKETPLACE_NAME; use codex_core_plugins::installed_marketplaces::installed_marketplace_roots_from_layer_stack; diff --git a/codex-rs/core/src/plugins/manager_tests.rs b/codex-rs/core/src/plugins/manager_tests.rs index c8bbba01b..2c5c6805b 100644 --- a/codex-rs/core/src/plugins/manager_tests.rs +++ b/codex-rs/core/src/plugins/manager_tests.rs @@ -1,10 +1,6 @@ use super::*; use crate::config::CONFIG_TOML_FILE; use crate::config::ConfigBuilder; -use crate::config_loader::ConfigLayerEntry; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; use crate::plugins::LoadedPlugin; use crate::plugins::PluginLoadOutcome; use crate::plugins::test_support::TEST_CURATED_PLUGIN_CACHE_VERSION; @@ -13,6 +9,10 @@ use crate::plugins::test_support::write_curated_plugin_sha_with as write_curated use crate::plugins::test_support::write_file; use crate::plugins::test_support::write_openai_curated_marketplace; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStack; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; use codex_config::McpServerConfig; use codex_config::types::McpServerTransportConfig; use codex_core_plugins::installed_marketplaces::marketplace_install_root; diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index 16f4f9b6b..8055b8f3a 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -14,14 +14,14 @@ use crate::session::session::Session; use crate::session::session::SessionSettingsUpdate; use crate::config::Config; -use crate::config_loader::CloudRequirementsLoader; -use crate::config_loader::LoaderOverrides; -use crate::config_loader::load_config_layers_state; use crate::realtime_context::REALTIME_TURN_TOKEN_BUDGET; use crate::realtime_context::truncate_realtime_text_to_token_budget; use crate::realtime_conversation::REALTIME_USER_TEXT_PREFIX; use crate::realtime_conversation::prefix_realtime_v2_text; use crate::session::spawn_review_thread; +use codex_config::CloudRequirementsLoader; +use codex_config::LoaderOverrides; +use codex_config::loader::load_config_layers_state; use codex_exec_server::LOCAL_FS; use codex_features::Feature; use codex_utils_absolute_path::AbsolutePathBuf; diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 3eb6fdddf..866458a2c 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -176,8 +176,8 @@ use crate::context_manager::TotalTokenUsageBreakdown; use crate::thread_rollout_truncation::initial_history_has_prior_user_turns; use codex_config::CONFIG_TOML_FILE; use codex_config::types::McpServerConfig; -use codex_config::types::ShellEnvironmentPolicy; use codex_model_provider_info::ModelProviderInfo; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; #[cfg(test)] diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 0206ee460..6b1bddbb8 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -2,14 +2,6 @@ use super::turn_context::TurnEnvironment; use super::*; use crate::config::ConfigBuilder; use crate::config::test_config; -use crate::config_loader::ConfigLayerStack; -use crate::config_loader::ConfigLayerStackOrdering; -use crate::config_loader::NetworkConstraints; -use crate::config_loader::NetworkDomainPermissionToml; -use crate::config_loader::NetworkDomainPermissionsToml; -use crate::config_loader::RequirementSource; -use crate::config_loader::Sourced; -use crate::config_loader::project_trust_key; use crate::context::ContextualUserFragment; use crate::context::TurnAborted; use crate::exec::ExecCapturePolicy; @@ -19,6 +11,14 @@ use crate::skills::SkillRenderSideEffects; use crate::skills::render::SkillMetadataBudget; use crate::test_support::models_manager_with_provider; use crate::tools::format_exec_output_str; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::NetworkConstraints; +use codex_config::NetworkDomainPermissionToml; +use codex_config::NetworkDomainPermissionsToml; +use codex_config::RequirementSource; +use codex_config::Sourced; +use codex_config::loader::project_trust_key; use codex_features::Feature; use codex_features::Features; @@ -925,7 +925,7 @@ async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> an RequirementSource::CloudRequirements, )); let mut requirements_toml = config.config_layer_stack.requirements_toml().clone(); - requirements_toml.network = Some(crate::config_loader::NetworkRequirementsToml { + requirements_toml.network = Some(codex_config::NetworkRequirementsToml { enabled: Some(true), ..Default::default() }); diff --git a/codex-rs/core/src/session/tests/guardian_tests.rs b/codex-rs/core/src/session/tests/guardian_tests.rs index d76660b29..ed6c6b60e 100644 --- a/codex-rs/core/src/session/tests/guardian_tests.rs +++ b/codex-rs/core/src/session/tests/guardian_tests.rs @@ -1,8 +1,5 @@ use super::*; use crate::compact::InitialContextInjection; -use crate::config_loader::ConfigLayerEntry; -use crate::config_loader::ConfigRequirements; -use crate::config_loader::ConfigRequirementsToml; use crate::exec::ExecCapturePolicy; use crate::exec::ExecParams; use crate::exec_policy::ExecPolicyManager; @@ -13,6 +10,9 @@ use crate::tools::context::FunctionToolOutput; use crate::tools::context::ToolCallSource; use crate::turn_diff_tracker::TurnDiffTracker; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; use codex_exec_server::EnvironmentManager; use codex_execpolicy::Decision; use codex_execpolicy::Evaluation; diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index 705a9ecb4..ee1da9b00 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -18,7 +18,6 @@ use crate::tools::handlers::multi_agents_v2::SendMessageHandler as SendMessageHa use crate::tools::handlers::multi_agents_v2::SpawnAgentHandler as SpawnAgentHandlerV2; use crate::tools::handlers::multi_agents_v2::WaitAgentHandler as WaitAgentHandlerV2; use crate::turn_diff_tracker::TurnDiffTracker; -use codex_config::types::ShellEnvironmentPolicy; use codex_features::Feature; use codex_login::AuthManager; use codex_login::CodexAuth; @@ -26,6 +25,7 @@ use codex_model_provider::create_model_provider; use codex_model_provider_info::built_in_model_providers; use codex_protocol::AgentPath; use codex_protocol::ThreadId; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::models::BaseInstructions; use codex_protocol::models::ContentItem; use codex_protocol::models::FunctionCallOutputBody; diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index bd4452ce1..b1b5c62b0 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -50,7 +50,7 @@ use crate::unified_exec::process::OutputBuffer; use crate::unified_exec::process::OutputHandles; use crate::unified_exec::process::SpawnLifecycleHandle; use crate::unified_exec::process::UnifiedExecProcess; -use codex_config::types::ShellEnvironmentPolicy; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::error::CodexErr; use codex_protocol::error::SandboxErr; use codex_protocol::protocol::ExecCommandSource; diff --git a/codex-rs/core/src/unified_exec/process_manager_tests.rs b/codex-rs/core/src/unified_exec/process_manager_tests.rs index 955c37bd5..78b004795 100644 --- a/codex-rs/core/src/unified_exec/process_manager_tests.rs +++ b/codex-rs/core/src/unified_exec/process_manager_tests.rs @@ -91,7 +91,7 @@ fn exec_server_params_use_env_policy_overlay_contract() { ]), exec_server_env_config: Some(ExecServerEnvConfig { policy: codex_exec_server::ExecEnvPolicy { - inherit: codex_config::types::ShellEnvironmentPolicyInherit::Core, + inherit: codex_protocol::config_types::ShellEnvironmentPolicyInherit::Core, ignore_default_excludes: false, exclude: Vec::new(), r#set: HashMap::new(), diff --git a/codex-rs/core/tests/suite/approvals.rs b/codex-rs/core/tests/suite/approvals.rs index 9358506a2..0888c91c4 100644 --- a/codex-rs/core/tests/suite/approvals.rs +++ b/codex-rs/core/tests/suite/approvals.rs @@ -2,15 +2,15 @@ use anyhow::Context; use anyhow::Result; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::NetworkConstraints; +use codex_config::NetworkRequirementsToml; +use codex_config::RequirementSource; +use codex_config::Sourced; use codex_config::types::ApprovalsReviewer; use codex_core::CodexThread; use codex_core::config::Constrained; -use codex_core::config_loader::ConfigLayerStack; -use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::config_loader::NetworkConstraints; -use codex_core::config_loader::NetworkRequirementsToml; -use codex_core::config_loader::RequirementSource; -use codex_core::config_loader::Sourced; use codex_core::sandboxing::SandboxPermissions; use codex_features::Feature; use codex_protocol::approvals::NetworkApprovalProtocol; diff --git a/codex-rs/core/tests/suite/deprecation_notice.rs b/codex-rs/core/tests/suite/deprecation_notice.rs index dc7280ea3..0ef7ddc33 100644 --- a/codex-rs/core/tests/suite/deprecation_notice.rs +++ b/codex-rs/core/tests/suite/deprecation_notice.rs @@ -2,10 +2,10 @@ use anyhow::Ok; use codex_app_server_protocol::ConfigLayerSource; -use codex_core::config_loader::ConfigLayerEntry; -use codex_core::config_loader::ConfigLayerStack; -use codex_core::config_loader::ConfigRequirements; -use codex_core::config_loader::ConfigRequirementsToml; +use codex_config::ConfigLayerEntry; +use codex_config::ConfigLayerStack; +use codex_config::ConfigRequirements; +use codex_config::ConfigRequirementsToml; use codex_features::Feature; use codex_protocol::protocol::DeprecationNoticeEvent; use codex_protocol::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index c683d353a..851980c42 100644 --- a/codex-rs/core/tests/suite/hooks.rs +++ b/codex-rs/core/tests/suite/hooks.rs @@ -3,13 +3,13 @@ use std::path::Path; use anyhow::Context; use anyhow::Result; +use codex_config::ConfigLayerStack; +use codex_config::ConfigLayerStackOrdering; +use codex_config::NetworkConstraints; +use codex_config::NetworkRequirementsToml; +use codex_config::RequirementSource; +use codex_config::Sourced; use codex_core::config::Constrained; -use codex_core::config_loader::ConfigLayerStack; -use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::config_loader::NetworkConstraints; -use codex_core::config_loader::NetworkRequirementsToml; -use codex_core::config_loader::RequirementSource; -use codex_core::config_loader::Sourced; use codex_features::Feature; use codex_protocol::items::parse_hook_prompt_fragment; use codex_protocol::models::ContentItem; diff --git a/codex-rs/core/tests/suite/permissions_messages.rs b/codex-rs/core/tests/suite/permissions_messages.rs index e3c04361b..1380f6162 100644 --- a/codex-rs/core/tests/suite/permissions_messages.rs +++ b/codex-rs/core/tests/suite/permissions_messages.rs @@ -1,7 +1,7 @@ use anyhow::Result; +use codex_config::ConfigLayerStack; use codex_core::ForkSnapshot; use codex_core::config::Constrained; -use codex_core::config_loader::ConfigLayerStack; use codex_core::context::ContextualUserFragment; use codex_core::context::PermissionsInstructions; use codex_core::load_exec_policy; diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index a1a25e6e9..21701d518 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -17,7 +17,6 @@ base64 = { workspace = true } bytes = { workspace = true } codex-app-server-protocol = { workspace = true } codex-client = { workspace = true } -codex-config = { workspace = true } codex-protocol = { workspace = true } codex-sandboxing = { workspace = true } codex-utils-absolute-path = { workspace = true } diff --git a/codex-rs/exec-server/src/local_process.rs b/codex-rs/exec-server/src/local_process.rs index bc9b2ba20..bc69ec610 100644 --- a/codex-rs/exec-server/src/local_process.rs +++ b/codex-rs/exec-server/src/local_process.rs @@ -6,9 +6,9 @@ use std::time::Duration; use async_trait::async_trait; use codex_app_server_protocol::JSONRPCErrorError; -use codex_config::shell_environment; -use codex_config::types::EnvironmentVariablePattern; -use codex_config::types::ShellEnvironmentPolicy; +use codex_protocol::config_types::EnvironmentVariablePattern; +use codex_protocol::config_types::ShellEnvironmentPolicy; +use codex_protocol::shell_environment; use codex_utils_pty::ExecCommandSession; use codex_utils_pty::TerminalSize; use tokio::sync::Mutex; @@ -706,7 +706,7 @@ fn notification_sender(inner: &Inner) -> Option { #[cfg(test)] mod tests { use super::*; - use codex_config::types::ShellEnvironmentPolicyInherit; + use codex_protocol::config_types::ShellEnvironmentPolicyInherit; use codex_utils_pty::ProcessDriver; use pretty_assertions::assert_eq; use tokio::sync::oneshot; diff --git a/codex-rs/exec-server/src/protocol.rs b/codex-rs/exec-server/src/protocol.rs index 435187d05..e801a7f43 100644 --- a/codex-rs/exec-server/src/protocol.rs +++ b/codex-rs/exec-server/src/protocol.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use crate::FileSystemSandboxContext; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; -use codex_config::types::ShellEnvironmentPolicyInherit; +use codex_protocol::config_types::ShellEnvironmentPolicyInherit; use codex_utils_absolute_path::AbsolutePathBuf; use serde::Deserialize; use serde::Serialize; diff --git a/codex-rs/exec/Cargo.toml b/codex-rs/exec/Cargo.toml index 0ec5d9b3c..632e47940 100644 --- a/codex-rs/exec/Cargo.toml +++ b/codex-rs/exec/Cargo.toml @@ -27,6 +27,7 @@ codex-arg0 = { workspace = true } codex-app-server-client = { workspace = true } codex-app-server-protocol = { workspace = true } codex-cloud-requirements = { workspace = true } +codex-config = { workspace = true } codex-core = { workspace = true } codex-feedback = { workspace = true } codex-git-utils = { workspace = true } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index c96e06279..204be3d97 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -52,6 +52,9 @@ use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnStartedNotification; use codex_arg0::Arg0DispatchPaths; use codex_cloud_requirements::cloud_requirements_loader_for_storage; +use codex_config::ConfigLoadError; +use codex_config::LoaderOverrides; +use codex_config::format_config_error_with_source; use codex_core::check_execpolicy_for_warnings; use codex_core::config::Config; use codex_core::config::ConfigBuilder; @@ -59,9 +62,6 @@ use codex_core::config::ConfigOverrides; use codex_core::config::find_codex_home; use codex_core::config::load_config_as_toml_with_cli_and_loader_overrides; use codex_core::config::resolve_oss_provider; -use codex_core::config_loader::ConfigLoadError; -use codex_core::config_loader::LoaderOverrides; -use codex_core::config_loader::format_config_error_with_source; use codex_core::find_thread_meta_by_name_str; use codex_core::format_exec_policy_error_with_source; use codex_core::path_utils; diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml index c624251e6..519ae5138 100644 --- a/codex-rs/linux-sandbox/Cargo.toml +++ b/codex-rs/linux-sandbox/Cargo.toml @@ -29,7 +29,6 @@ serde_json = { workspace = true } url = { workspace = true } [target.'cfg(target_os = "linux")'.dev-dependencies] -codex-config = { workspace = true } codex-core = { workspace = true } pretty_assertions = { workspace = true } tempfile = { workspace = true } diff --git a/codex-rs/linux-sandbox/tests/suite/landlock.rs b/codex-rs/linux-sandbox/tests/suite/landlock.rs index 38478e11f..d1e84b89e 100644 --- a/codex-rs/linux-sandbox/tests/suite/landlock.rs +++ b/codex-rs/linux-sandbox/tests/suite/landlock.rs @@ -1,11 +1,11 @@ #![cfg(target_os = "linux")] #![allow(clippy::unwrap_used)] -use codex_config::types::ShellEnvironmentPolicy; use codex_core::exec::ExecCapturePolicy; use codex_core::exec::ExecParams; use codex_core::exec::process_exec_tool_call; use codex_core::exec_env::create_env; use codex_core::sandboxing::SandboxPermissions; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::error::CodexErr; use codex_protocol::error::Result; diff --git a/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs b/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs index 256373953..e906facac 100644 --- a/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs +++ b/codex-rs/linux-sandbox/tests/suite/managed_proxy.rs @@ -1,8 +1,8 @@ #![cfg(target_os = "linux")] #![allow(clippy::unwrap_used)] -use codex_config::types::ShellEnvironmentPolicy; use codex_core::exec_env::create_env; +use codex_protocol::config_types::ShellEnvironmentPolicy; use codex_protocol::protocol::SandboxPolicy; use pretty_assertions::assert_eq; use std::collections::HashMap; diff --git a/codex-rs/protocol/Cargo.toml b/codex-rs/protocol/Cargo.toml index 2bd46d7d5..1de72dda3 100644 --- a/codex-rs/protocol/Cargo.toml +++ b/codex-rs/protocol/Cargo.toml @@ -44,6 +44,7 @@ ts-rs = { workspace = true, features = [ "no-serde-warnings", ] } uuid = { workspace = true, features = ["serde", "v7", "v4"] } +wildmatch = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] landlock = { workspace = true } diff --git a/codex-rs/protocol/src/config_types.rs b/codex-rs/protocol/src/config_types.rs index 2be5c6f12..da83ee858 100644 --- a/codex-rs/protocol/src/config_types.rs +++ b/codex-rs/protocol/src/config_types.rs @@ -8,11 +8,13 @@ use schemars::schema::SchemaObject; use serde::Deserialize; use serde::Serialize; use serde_json::Value; +use std::collections::HashMap; use std::num::NonZeroU64; use std::time::Duration; use strum_macros::Display; use strum_macros::EnumIter; use ts_rs::TS; +use wildmatch::WildMatchPattern; use crate::openai_models::ReasoningEffort; @@ -105,6 +107,65 @@ impl JsonSchema for ApprovalsReviewer { } } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] +#[serde(rename_all = "kebab-case")] +pub enum ShellEnvironmentPolicyInherit { + /// "Core" environment variables for the platform. On UNIX, this would + /// include HOME, LOGNAME, PATH, SHELL, and USER, among others. + Core, + + /// Inherits the full environment from the parent process. + #[default] + All, + + /// Do not inherit any environment variables from the parent process. + None, +} + +pub type EnvironmentVariablePattern = WildMatchPattern<'*', '?'>; + +/// Deriving the `env` based on this policy works as follows: +/// 1. Create an initial map based on the `inherit` policy. +/// 2. If `ignore_default_excludes` is false, filter the map using the default +/// exclude pattern(s), which are: `"*KEY*"`, `"*SECRET*"`, and `"*TOKEN*"`. +/// 3. If `exclude` is not empty, filter the map using the provided patterns. +/// 4. Insert any entries from `r#set` into the map. +/// 5. If non-empty, filter the map using the `include_only` patterns. +#[derive(Debug, Clone, PartialEq)] +pub struct ShellEnvironmentPolicy { + /// Starting point when building the environment. + pub inherit: ShellEnvironmentPolicyInherit, + + /// True to skip the check to exclude default environment variables that + /// contain "KEY", "SECRET", or "TOKEN" in their name. Defaults to true. + pub ignore_default_excludes: bool, + + /// Environment variable names to exclude from the environment. + pub exclude: Vec, + + /// (key, value) pairs to insert in the environment. + pub r#set: HashMap, + + /// Environment variable names to retain in the environment. + pub include_only: Vec, + + /// If true, the shell profile will be used to run the command. + pub use_profile: bool, +} + +impl Default for ShellEnvironmentPolicy { + fn default() -> Self { + Self { + inherit: ShellEnvironmentPolicyInherit::All, + ignore_default_excludes: true, + exclude: Vec::new(), + r#set: HashMap::new(), + include_only: Vec::new(), + use_profile: false, + } + } +} + fn string_enum_schema_with_description(values: &[&str], description: &str) -> Schema { let mut schema = SchemaObject { instance_type: Some(InstanceType::String.into()), diff --git a/codex-rs/protocol/src/lib.rs b/codex-rs/protocol/src/lib.rs index 2506dae74..175c92331 100644 --- a/codex-rs/protocol/src/lib.rs +++ b/codex-rs/protocol/src/lib.rs @@ -25,4 +25,5 @@ pub mod plan_tool; pub mod protocol; pub mod request_permissions; pub mod request_user_input; +pub mod shell_environment; pub mod user_input; diff --git a/codex-rs/config/src/shell_environment.rs b/codex-rs/protocol/src/shell_environment.rs similarity index 95% rename from codex-rs/config/src/shell_environment.rs rename to codex-rs/protocol/src/shell_environment.rs index 80fe0da42..2a7aace3e 100644 --- a/codex-rs/config/src/shell_environment.rs +++ b/codex-rs/protocol/src/shell_environment.rs @@ -1,6 +1,6 @@ -use crate::types::EnvironmentVariablePattern; -use crate::types::ShellEnvironmentPolicy; -use crate::types::ShellEnvironmentPolicyInherit; +use crate::config_types::EnvironmentVariablePattern; +use crate::config_types::ShellEnvironmentPolicy; +use crate::config_types::ShellEnvironmentPolicyInherit; use std::collections::HashMap; use std::collections::HashSet; @@ -76,7 +76,6 @@ where } }; - // Internal helper - does `name` match any pattern in `patterns`? let matches_any = |name: &str, patterns: &[EnvironmentVariablePattern]| -> bool { patterns.iter().any(|pattern| pattern.matches(name)) }; diff --git a/codex-rs/rmcp-client/src/stdio_server_launcher.rs b/codex-rs/rmcp-client/src/stdio_server_launcher.rs index b3d3b849d..ced594b78 100644 --- a/codex-rs/rmcp-client/src/stdio_server_launcher.rs +++ b/codex-rs/rmcp-client/src/stdio_server_launcher.rs @@ -28,10 +28,10 @@ use std::time::Duration; use anyhow::Result; use anyhow::anyhow; use codex_config::types::McpServerEnvVar; -use codex_config::types::ShellEnvironmentPolicyInherit; use codex_exec_server::ExecBackend; use codex_exec_server::ExecEnvPolicy; use codex_exec_server::ExecParams; +use codex_protocol::config_types::ShellEnvironmentPolicyInherit; #[cfg(unix)] use codex_utils_pty::process_group::kill_process_group; #[cfg(unix)] @@ -464,9 +464,9 @@ impl ExecutorStdioServerLauncher { #[cfg(test)] mod tests { use super::*; - use codex_config::shell_environment; - use codex_config::types::EnvironmentVariablePattern; - use codex_config::types::ShellEnvironmentPolicy; + use codex_protocol::config_types::EnvironmentVariablePattern; + use codex_protocol::config_types::ShellEnvironmentPolicy; + use codex_protocol::shell_environment; #[test] fn remote_env_policy_uses_core_env_without_remote_source_vars() {