From 2e22885e79bd793316da217929996149860fff43 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 19 Mar 2026 20:12:07 -0700 Subject: [PATCH] Split features into codex-features crate (#15253) - Split the feature system into a new `codex-features` crate. - Cut `codex-core` and workspace consumers over to the new config and warning APIs. Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com> Co-authored-by: Codex --- codex-rs/Cargo.lock | 24 ++++ codex-rs/Cargo.toml | 2 + codex-rs/app-server-client/Cargo.toml | 1 + codex-rs/app-server-client/src/lib.rs | 5 +- codex-rs/app-server/Cargo.toml | 1 + .../app-server/src/codex_message_processor.rs | 6 +- codex-rs/app-server/src/message_processor.rs | 3 +- codex-rs/app-server/tests/common/Cargo.toml | 1 + codex-rs/app-server/tests/common/config.rs | 4 +- .../suite/v2/experimental_feature_list.rs | 4 +- .../app-server/tests/suite/v2/plan_item.rs | 4 +- .../tests/suite/v2/realtime_conversation.rs | 4 +- .../tests/suite/v2/thread_shell_command.rs | 4 +- .../app-server/tests/suite/v2/turn_start.rs | 4 +- .../tests/suite/v2/turn_start_zsh_fork.rs | 4 +- codex-rs/cli/Cargo.toml | 1 + codex-rs/cli/src/main.rs | 17 +-- codex-rs/core/Cargo.toml | 1 + codex-rs/core/src/agent/control.rs | 2 +- codex-rs/core/src/agent/control_tests.rs | 2 +- codex-rs/core/src/codex.rs | 25 +++- codex-rs/core/src/codex_tests.rs | 3 +- codex-rs/core/src/codex_tests_guardian.rs | 2 +- codex-rs/core/src/codex_thread.rs | 2 +- codex-rs/core/src/config/config_tests.rs | 7 +- codex-rs/core/src/config/edit.rs | 2 +- codex-rs/core/src/config/managed_features.rs | 28 +++- codex-rs/core/src/config/mod.rs | 27 +++- codex-rs/core/src/config/profile.rs | 3 +- codex-rs/core/src/config/schema.rs | 5 +- codex-rs/core/src/connectors.rs | 2 +- codex-rs/core/src/connectors_tests.rs | 2 +- codex-rs/core/src/context_manager/updates.rs | 2 +- codex-rs/core/src/guardian/review_session.rs | 2 +- codex-rs/core/src/lib.rs | 1 - codex-rs/core/src/mcp/mod_tests.rs | 2 +- codex-rs/core/src/mcp/skill_dependencies.rs | 2 +- codex-rs/core/src/mcp_tool_call.rs | 2 +- codex-rs/core/src/memories/phase2.rs | 2 +- codex-rs/core/src/memories/start.rs | 2 +- .../core/src/models_manager/model_info.rs | 2 +- codex-rs/core/src/original_image_detail.rs | 4 +- .../core/src/original_image_detail_tests.rs | 2 +- codex-rs/core/src/otel_init.rs | 2 +- codex-rs/core/src/plugins/discoverable.rs | 2 +- codex-rs/core/src/plugins/manager.rs | 2 +- codex-rs/core/src/project_doc.rs | 2 +- codex-rs/core/src/project_doc_tests.rs | 2 +- codex-rs/core/src/rollout/recorder_tests.rs | 2 +- codex-rs/core/src/tasks/mod.rs | 2 +- codex-rs/core/src/tasks/review.rs | 2 +- codex-rs/core/src/tools/code_mode/service.rs | 2 +- codex-rs/core/src/tools/handlers/artifacts.rs | 2 +- codex-rs/core/src/tools/handlers/js_repl.rs | 2 +- .../core/src/tools/handlers/multi_agents.rs | 2 +- .../src/tools/handlers/multi_agents_tests.rs | 2 +- codex-rs/core/src/tools/handlers/shell.rs | 2 +- .../core/src/tools/handlers/unified_exec.rs | 2 +- codex-rs/core/src/tools/js_repl/mod_tests.rs | 2 +- codex-rs/core/src/tools/runtimes/shell.rs | 2 +- .../tools/runtimes/shell/unix_escalation.rs | 2 +- .../core/src/tools/runtimes/unified_exec.rs | 2 +- codex-rs/core/src/tools/spec.rs | 4 +- codex-rs/core/src/windows_sandbox.rs | 6 +- codex-rs/core/src/windows_sandbox_tests.rs | 4 +- codex-rs/core/tests/common/Cargo.toml | 2 + codex-rs/core/tests/common/lib.rs | 33 ++++- codex-rs/core/tests/common/test_codex.rs | 2 +- codex-rs/core/tests/common/zsh_fork.rs | 2 +- codex-rs/core/tests/suite/agent_jobs.rs | 2 +- codex-rs/core/tests/suite/agent_websocket.rs | 2 +- codex-rs/core/tests/suite/apply_patch_cli.rs | 2 +- codex-rs/core/tests/suite/approvals.rs | 2 +- codex-rs/core/tests/suite/client.rs | 2 +- .../core/tests/suite/client_websockets.rs | 2 +- codex-rs/core/tests/suite/code_mode.rs | 2 +- codex-rs/core/tests/suite/compact.rs | 5 +- .../core/tests/suite/deprecation_notice.rs | 2 +- codex-rs/core/tests/suite/exec_policy.rs | 2 +- .../core/tests/suite/hierarchical_agents.rs | 2 +- codex-rs/core/tests/suite/hooks.rs | 2 +- codex-rs/core/tests/suite/js_repl.rs | 2 +- codex-rs/core/tests/suite/memories.rs | 2 +- codex-rs/core/tests/suite/model_switching.rs | 2 +- .../core/tests/suite/model_visible_layout.rs | 2 +- codex-rs/core/tests/suite/otel.rs | 2 +- codex-rs/core/tests/suite/personality.rs | 2 +- codex-rs/core/tests/suite/plugins.rs | 2 +- codex-rs/core/tests/suite/prompt_caching.rs | 2 +- .../core/tests/suite/request_compression.rs | 2 +- .../core/tests/suite/request_permissions.rs | 2 +- .../tests/suite/request_permissions_tool.rs | 2 +- .../core/tests/suite/request_user_input.rs | 2 +- codex-rs/core/tests/suite/search_tool.rs | 2 +- codex-rs/core/tests/suite/shell_command.rs | 2 +- codex-rs/core/tests/suite/shell_snapshot.rs | 2 +- .../tests/suite/spawn_agent_description.rs | 2 +- codex-rs/core/tests/suite/sqlite_state.rs | 2 +- .../tests/suite/subagent_notifications.rs | 2 +- codex-rs/core/tests/suite/tool_harness.rs | 2 +- codex-rs/core/tests/suite/tools.rs | 2 +- codex-rs/core/tests/suite/undo.rs | 2 +- codex-rs/core/tests/suite/unified_exec.rs | 2 +- .../tests/suite/unstable_features_warning.rs | 2 +- codex-rs/core/tests/suite/user_shell_cmd.rs | 2 +- codex-rs/core/tests/suite/view_image.rs | 2 +- codex-rs/core/tests/suite/web_search.rs | 2 +- codex-rs/features/BUILD.bazel | 16 +++ codex-rs/features/Cargo.toml | 25 ++++ .../src/features => features/src}/legacy.rs | 8 +- .../src/features.rs => features/src/lib.rs} | 124 ++++++++---------- .../src/tests.rs} | 86 +++++++++++- codex-rs/mcp-server/Cargo.toml | 1 + codex-rs/mcp-server/src/message_processor.rs | 3 +- codex-rs/tui/Cargo.toml | 1 + codex-rs/tui/src/app.rs | 2 +- codex-rs/tui/src/app_event.rs | 2 +- codex-rs/tui/src/app_server_tui_dispatch.rs | 2 +- .../tui/src/bottom_pane/approval_overlay.rs | 2 +- .../bottom_pane/experimental_features_view.rs | 2 +- codex-rs/tui/src/bottom_pane/mod.rs | 2 +- codex-rs/tui/src/chatwidget.rs | 4 +- codex-rs/tui/src/chatwidget/tests.rs | 4 +- codex-rs/tui/src/lib.rs | 2 +- codex-rs/tui/src/tooltips.rs | 2 +- codex-rs/tui_app_server/Cargo.toml | 1 + codex-rs/tui_app_server/src/app.rs | 2 +- codex-rs/tui_app_server/src/app_event.rs | 2 +- .../src/bottom_pane/approval_overlay.rs | 2 +- .../bottom_pane/experimental_features_view.rs | 2 +- .../tui_app_server/src/bottom_pane/mod.rs | 2 +- codex-rs/tui_app_server/src/chatwidget.rs | 4 +- .../tui_app_server/src/chatwidget/tests.rs | 4 +- codex-rs/tui_app_server/src/lib.rs | 2 +- codex-rs/tui_app_server/src/tooltips.rs | 2 +- 135 files changed, 456 insertions(+), 250 deletions(-) create mode 100644 codex-rs/features/BUILD.bazel create mode 100644 codex-rs/features/Cargo.toml rename codex-rs/{core/src/features => features/src}/legacy.rs (95%) rename codex-rs/{core/src/features.rs => features/src/lib.rs} (90%) rename codex-rs/{core/src/features_tests.rs => features/src/tests.rs} (68%) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index c5ce0ebe7..880fd87ba 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -409,6 +409,7 @@ dependencies = [ "chrono", "codex-app-server-protocol", "codex-core", + "codex-features", "codex-protocol", "codex-utils-cargo-bin", "core_test_support", @@ -1428,6 +1429,7 @@ dependencies = [ "codex-cloud-requirements", "codex-core", "codex-exec-server", + "codex-features", "codex-feedback", "codex-file-search", "codex-login", @@ -1474,6 +1476,7 @@ dependencies = [ "codex-app-server-protocol", "codex-arg0", "codex-core", + "codex-features", "codex-feedback", "codex-protocol", "futures", @@ -1657,6 +1660,7 @@ dependencies = [ "codex-core", "codex-exec", "codex-execpolicy", + "codex-features", "codex-login", "codex-mcp-server", "codex-protocol", @@ -1845,6 +1849,7 @@ dependencies = [ "codex-connectors", "codex-exec-server", "codex-execpolicy", + "codex-features", "codex-file-search", "codex-git", "codex-hooks", @@ -2060,6 +2065,20 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "codex-features" +version = "0.0.0" +dependencies = [ + "codex-login", + "codex-otel", + "codex-protocol", + "pretty_assertions", + "schemars 0.8.22", + "serde", + "toml 0.9.11+spec-1.1.0", + "tracing", +] + [[package]] name = "codex-feedback" version = "0.0.0" @@ -2209,6 +2228,7 @@ dependencies = [ "anyhow", "codex-arg0", "codex-core", + "codex-features", "codex-protocol", "codex-shell-command", "codex-utils-cli", @@ -2554,6 +2574,7 @@ dependencies = [ "codex-client", "codex-cloud-requirements", "codex-core", + "codex-features", "codex-feedback", "codex-file-search", "codex-login", @@ -2646,6 +2667,7 @@ dependencies = [ "codex-client", "codex-cloud-requirements", "codex-core", + "codex-features", "codex-feedback", "codex-file-search", "codex-login", @@ -3096,7 +3118,9 @@ dependencies = [ "anyhow", "assert_cmd", "base64 0.22.1", + "codex-arg0", "codex-core", + "codex-features", "codex-protocol", "codex-utils-absolute-path", "codex-utils-cargo-bin", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 961c4ef9f..331174a80 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -11,6 +11,7 @@ members = [ "apply-patch", "arg0", "feedback", + "features", "codex-backend-openapi-models", "cloud-requirements", "cloud-tasks", @@ -110,6 +111,7 @@ codex-exec-server = { path = "exec-server" } codex-execpolicy = { path = "execpolicy" } codex-experimental-api-macros = { path = "codex-experimental-api-macros" } codex-feedback = { path = "feedback" } +codex-features = { path = "features" } codex-file-search = { path = "file-search" } codex-git = { path = "utils/git" } codex-hooks = { path = "hooks" } diff --git a/codex-rs/app-server-client/Cargo.toml b/codex-rs/app-server-client/Cargo.toml index a0b98c0fe..5a3a1aa73 100644 --- a/codex-rs/app-server-client/Cargo.toml +++ b/codex-rs/app-server-client/Cargo.toml @@ -16,6 +16,7 @@ codex-app-server = { workspace = true } codex-app-server-protocol = { workspace = true } codex-arg0 = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-feedback = { workspace = true } codex-protocol = { workspace = true } futures = { workspace = true } diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 1452eb590..acf9c77a1 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -47,6 +47,7 @@ use codex_core::config::Config; use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; +use codex_features::Feature; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; use serde::de::DeserializeOwned; @@ -215,7 +216,7 @@ impl InProcessClientStartArgs { default_mode_request_user_input: self .config .features - .enabled(codex_core::features::Feature::DefaultModeRequestUserInput), + .enabled(Feature::DefaultModeRequestUserInput), }, )); @@ -1484,7 +1485,7 @@ mod tests { CollaborationModesConfig { default_mode_request_user_input: config .features - .enabled(codex_core::features::Feature::DefaultModeRequestUserInput), + .enabled(Feature::DefaultModeRequestUserInput), }, )); event_tx diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index 939105049..c6be85984 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -33,6 +33,7 @@ codex-arg0 = { workspace = true } codex-cloud-requirements = { workspace = true } codex-core = { workspace = true } codex-exec-server = { workspace = true } +codex-features = { workspace = true } codex-otel = { workspace = true } codex-shell-command = { workspace = true } codex-utils-cli = { workspace = true } diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 66c0c61db..328827785 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -207,9 +207,6 @@ use codex_core::exec::ExecCapturePolicy; use codex_core::exec::ExecExpiration; use codex_core::exec::ExecParams; use codex_core::exec_env::create_env; -use codex_core::features::FEATURES; -use codex_core::features::Feature; -use codex_core::features::Stage; use codex_core::find_archived_thread_path_by_id_str; use codex_core::find_thread_name_by_id; use codex_core::find_thread_names_by_ids; @@ -240,6 +237,9 @@ use codex_core::state_db::reconcile_rollout; use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_core::windows_sandbox::WindowsSandboxSetupMode as CoreWindowsSandboxSetupMode; use codex_core::windows_sandbox::WindowsSandboxSetupRequest; +use codex_features::FEATURES; +use codex_features::Feature; +use codex_features::Stage; use codex_feedback::CodexFeedback; use codex_login::ServerOptions as LoginServerOptions; use codex_login::ShutdownHandle; diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 59841e3d5..2dd682439 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -59,6 +59,7 @@ use codex_core::default_client::get_codex_user_agent; use codex_core::default_client::set_default_client_residency_requirement; use codex_core::default_client::set_default_originator; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; +use codex_features::Feature; use codex_feedback::CodexFeedback; use codex_login::auth::ExternalAuthRefreshContext; use codex_login::auth::ExternalAuthRefreshReason; @@ -212,7 +213,7 @@ impl MessageProcessor { CollaborationModesConfig { default_mode_request_user_input: config .features - .enabled(codex_core::features::Feature::DefaultModeRequestUserInput), + .enabled(Feature::DefaultModeRequestUserInput), }, )); (auth_manager, thread_manager) diff --git a/codex-rs/app-server/tests/common/Cargo.toml b/codex-rs/app-server/tests/common/Cargo.toml index de58509f0..851ba9556 100644 --- a/codex-rs/app-server/tests/common/Cargo.toml +++ b/codex-rs/app-server/tests/common/Cargo.toml @@ -13,6 +13,7 @@ base64 = { workspace = true } chrono = { workspace = true } codex-app-server-protocol = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-protocol = { workspace = true } codex-utils-cargo-bin = { workspace = true } serde = { workspace = true } diff --git a/codex-rs/app-server/tests/common/config.rs b/codex-rs/app-server/tests/common/config.rs index 7784f36e9..deb16c632 100644 --- a/codex-rs/app-server/tests/common/config.rs +++ b/codex-rs/app-server/tests/common/config.rs @@ -1,5 +1,5 @@ -use codex_core::features::FEATURES; -use codex_core::features::Feature; +use codex_features::FEATURES; +use codex_features::Feature; use std::collections::BTreeMap; use std::path::Path; 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 58deb5f82..7ff5f6fe3 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 @@ -10,8 +10,8 @@ use codex_app_server_protocol::ExperimentalFeatureStage; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; use codex_core::config::ConfigBuilder; -use codex_core::features::FEATURES; -use codex_core::features::Stage; +use codex_features::FEATURES; +use codex_features::Stage; use pretty_assertions::assert_eq; use tempfile::TempDir; use tokio::time::timeout; diff --git a/codex-rs/app-server/tests/suite/v2/plan_item.rs b/codex-rs/app-server/tests/suite/v2/plan_item.rs index 58471f434..0ed93cbea 100644 --- a/codex-rs/app-server/tests/suite/v2/plan_item.rs +++ b/codex-rs/app-server/tests/suite/v2/plan_item.rs @@ -18,8 +18,8 @@ use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::UserInput as V2UserInput; -use codex_core::features::FEATURES; -use codex_core::features::Feature; +use codex_features::FEATURES; +use codex_features::Feature; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Settings; diff --git a/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs b/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs index bfb28a227..1073c1b93 100644 --- a/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs +++ b/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs @@ -23,8 +23,8 @@ use codex_app_server_protocol::ThreadRealtimeStopParams; use codex_app_server_protocol::ThreadRealtimeStopResponse; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; -use codex_core::features::FEATURES; -use codex_core::features::Feature; +use codex_features::FEATURES; +use codex_features::Feature; use codex_protocol::protocol::RealtimeConversationVersion; use core_test_support::responses::start_websocket_server; use core_test_support::skip_if_no_network; diff --git a/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs b/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs index e6dd21796..5b58796bf 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs @@ -26,8 +26,8 @@ use codex_app_server_protocol::TurnCompletedNotification; use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::UserInput as V2UserInput; -use codex_core::features::FEATURES; -use codex_core::features::Feature; +use codex_features::FEATURES; +use codex_features::Feature; use pretty_assertions::assert_eq; use std::collections::BTreeMap; use std::path::Path; diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 6232763c8..8d7ca0261 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -44,9 +44,9 @@ use codex_app_server_protocol::TurnStartedNotification; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::UserInput as V2UserInput; use codex_core::config::ConfigToml; -use codex_core::features::FEATURES; -use codex_core::features::Feature; use codex_core::personality_migration::PERSONALITY_MIGRATION_FILENAME; +use codex_features::FEATURES; +use codex_features::Feature; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Personality; diff --git a/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs b/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs index 559be8b18..c8ae882e2 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs @@ -30,8 +30,8 @@ use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::UserInput as V2UserInput; -use codex_core::features::FEATURES; -use codex_core::features::Feature; +use codex_features::FEATURES; +use codex_features::Feature; use core_test_support::responses; use core_test_support::skip_if_no_network; use pretty_assertions::assert_eq; diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index affc5ef8c..c2fd1300c 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -30,6 +30,7 @@ codex-config = { workspace = true } codex-core = { workspace = true } codex-exec = { workspace = true } codex-execpolicy = { workspace = true } +codex-features = { workspace = true } codex-login = { workspace = true } codex-mcp-server = { workspace = true } codex-protocol = { workspace = true } diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index e9f4d6f68..8446e457b 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -48,8 +48,9 @@ use codex_core::config::Config; use codex_core::config::ConfigOverrides; use codex_core::config::edit::ConfigEditsBuilder; use codex_core::config::find_codex_home; -use codex_core::features::Stage; -use codex_core::features::is_known_feature_key; +use codex_features::FEATURES; +use codex_features::Stage; +use codex_features::is_known_feature_key; use codex_terminal_detection::TerminalName; /// Codex CLI @@ -569,8 +570,7 @@ struct FeatureSetArgs { feature: String, } -fn stage_str(stage: codex_core::features::Stage) -> &'static str { - use codex_core::features::Stage; +fn stage_str(stage: Stage) -> &'static str { match stage { Stage::UnderDevelopment => "under development", Stage::Experimental { .. } => "experimental", @@ -886,10 +886,10 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> { overrides, ) .await?; - let mut rows = Vec::with_capacity(codex_core::features::FEATURES.len()); + let mut rows = Vec::with_capacity(FEATURES.len()); let mut name_width = 0; let mut stage_width = 0; - for def in codex_core::features::FEATURES.iter() { + for def in FEATURES { let name = def.key; let stage = stage_str(def.stage); let enabled = config.features.enabled(def.id); @@ -951,10 +951,7 @@ fn maybe_print_under_development_feature_warning( return; } - let Some(spec) = codex_core::features::FEATURES - .iter() - .find(|spec| spec.key == feature) - else { + let Some(spec) = FEATURES.iter().find(|spec| spec.key == feature) else { return; }; if !matches!(spec.stage, Stage::UnderDevelopment) { diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 7a817609b..d648655b2 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -34,6 +34,7 @@ codex-async-utils = { workspace = true } codex-connectors = { workspace = true } codex-config = { workspace = true } codex-exec-server = { workspace = true } +codex-features = { workspace = true } codex-login = { workspace = true } codex-shell-command = { workspace = true } codex-skills = { workspace = true } diff --git a/codex-rs/core/src/agent/control.rs b/codex-rs/core/src/agent/control.rs index 83af6258f..d75fc8952 100644 --- a/codex-rs/core/src/agent/control.rs +++ b/codex-rs/core/src/agent/control.rs @@ -6,7 +6,6 @@ use crate::agent::status::is_final; use crate::codex_thread::ThreadConfigSnapshot; use crate::error::CodexErr; use crate::error::Result as CodexResult; -use crate::features::Feature; use crate::find_archived_thread_path_by_id_str; use crate::find_thread_path_by_id_str; use crate::rollout::RolloutRecorder; @@ -15,6 +14,7 @@ use crate::session_prefix::format_subagent_notification_message; use crate::shell_snapshot::ShellSnapshot; use crate::state_db; use crate::thread_manager::ThreadManagerState; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseItem; diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 7c2c46b2f..24344db71 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -8,9 +8,9 @@ use crate::config::Config; use crate::config::ConfigBuilder; use crate::config_loader::LoaderOverrides; use crate::contextual_user_message::SUBAGENT_NOTIFICATION_OPEN_TAG; -use crate::features::Feature; use assert_matches::assert_matches; use chrono::Utc; +use codex_features::Feature; use codex_protocol::config_types::ModeKind; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 3b1d2610d..12270bb6d 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -27,9 +27,6 @@ use crate::compact_remote::run_inline_remote_auto_compact_task; use crate::config::ManagedFeatures; use crate::connectors; use crate::exec_policy::ExecPolicyManager; -use crate::features::FEATURES; -use crate::features::Feature; -use crate::features::maybe_push_unstable_features_warning; #[cfg(test)] use crate::models_manager::collaboration_mode_presets::CollaborationModesConfig; use crate::models_manager::manager::ModelsManager; @@ -59,6 +56,9 @@ use chrono::Utc; use codex_app_server_protocol::McpServerElicitationRequest; use codex_app_server_protocol::McpServerElicitationRequestParams; use codex_exec_server::Environment; +use codex_features::FEATURES; +use codex_features::Feature; +use codex_features::unstable_features_warning_event; use codex_hooks::HookEvent; use codex_hooks::HookEventAfterAgent; use codex_hooks::HookPayload; @@ -140,6 +140,7 @@ use tokio::sync::oneshot; use tokio::sync::watch; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; +use toml::Value as TomlValue; use tracing::Instrument; use tracing::debug; use tracing::debug_span; @@ -1568,7 +1569,19 @@ impl Session { }), }); } - maybe_push_unstable_features_warning(&config, &mut post_session_configured_events); + let config_path = config.codex_home.join(CONFIG_TOML_FILE); + if let Some(event) = unstable_features_warning_event( + config + .config_layer_stack + .effective_config() + .get("features") + .and_then(TomlValue::as_table), + config.suppress_unstable_features_warning, + &config.features, + &config_path.display().to_string(), + ) { + post_session_configured_events.push(event); + } if config.permissions.approval_policy.value() == AskForApproval::OnFailure { post_session_configured_events.push(Event { id: "".to_owned(), @@ -5163,8 +5176,8 @@ async fn spawn_review_thread( .await; // For reviews, disable web_search and view_image regardless of global settings. let mut review_features = sess.features.clone(); - let _ = review_features.disable(crate::features::Feature::WebSearchRequest); - let _ = review_features.disable(crate::features::Feature::WebSearchCached); + let _ = review_features.disable(Feature::WebSearchRequest); + let _ = review_features.disable(Feature::WebSearchCached); let review_web_search_mode = WebSearchMode::Disabled; let tools_config = ToolsConfig::new(&ToolsConfigParams { model_info: &review_model_info, diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index 9cf5ce372..a814eab95 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -15,6 +15,7 @@ use crate::models_manager::model_info; use crate::shell::default_user_shell; use crate::tools::format_exec_output_str; +use codex_features::Features; use codex_protocol::ThreadId; use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; @@ -3409,7 +3410,7 @@ async fn refresh_mcp_servers_is_deferred_until_next_turn() { #[tokio::test] async fn record_model_warning_appends_user_message() { let (mut session, turn_context) = make_session_and_context().await; - let features = crate::features::Features::with_defaults().into(); + let features = Features::with_defaults().into(); session.features = features; session diff --git a/codex-rs/core/src/codex_tests_guardian.rs b/codex-rs/core/src/codex_tests_guardian.rs index cfdd6ca61..af0fccc9a 100644 --- a/codex-rs/core/src/codex_tests_guardian.rs +++ b/codex-rs/core/src/codex_tests_guardian.rs @@ -6,7 +6,6 @@ use crate::config_loader::ConfigRequirementsToml; use crate::exec::ExecCapturePolicy; use crate::exec::ExecParams; use crate::exec_policy::ExecPolicyManager; -use crate::features::Feature; use crate::guardian::GUARDIAN_REVIEWER_NAME; use crate::protocol::AskForApproval; use crate::sandboxing::SandboxPermissions; @@ -16,6 +15,7 @@ use codex_app_server_protocol::ConfigLayerSource; use codex_execpolicy::Decision; use codex_execpolicy::Evaluation; use codex_execpolicy::RuleMatch; +use codex_features::Feature; use codex_protocol::models::ContentItem; use codex_protocol::models::NetworkPermissions; use codex_protocol::models::PermissionProfile; diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index 2bd9608b9..e016fec97 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -4,11 +4,11 @@ use crate::codex::SteerInputError; use crate::config::ConstraintResult; use crate::error::CodexErr; use crate::error::Result as CodexResult; -use crate::features::Feature; use crate::file_watcher::WatchRegistration; use crate::protocol::Event; use crate::protocol::Op; use crate::protocol::Submission; +use codex_features::Feature; use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ServiceTier; diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 667d38195..1c78759ec 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -13,9 +13,10 @@ use crate::config::types::NotificationMethod; use crate::config::types::Notifications; use crate::config::types::ToolSuggestDiscoverableType; use crate::config_loader::RequirementSource; -use crate::features::Feature; use assert_matches::assert_matches; use codex_config::CONFIG_TOML_FILE; +use codex_features::Feature; +use codex_features::FeaturesToml; use codex_protocol::permissions::FileSystemAccessMode; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; @@ -1662,7 +1663,7 @@ fn feature_table_overrides_legacy_flags() -> std::io::Result<()> { let mut entries = BTreeMap::new(); entries.insert("apply_patch_freeform".to_string(), false); let cfg = ConfigToml { - features: Some(crate::features::FeaturesToml { entries }), + features: Some(FeaturesToml { entries }), ..Default::default() }; @@ -1710,7 +1711,7 @@ fn responses_websocket_features_do_not_change_wire_api() -> std::io::Result<()> let mut entries = BTreeMap::new(); entries.insert(feature_key.to_string(), true); let cfg = ConfigToml { - features: Some(crate::features::FeaturesToml { entries }), + features: Some(FeaturesToml { entries }), ..Default::default() }; diff --git a/codex-rs/core/src/config/edit.rs b/codex-rs/core/src/config/edit.rs index 03f477ba0..2865ace4b 100644 --- a/codex-rs/core/src/config/edit.rs +++ b/codex-rs/core/src/config/edit.rs @@ -1,10 +1,10 @@ use crate::config::types::McpServerConfig; use crate::config::types::Notice; -use crate::features::FEATURES; use crate::path_utils::resolve_symlink_write_paths; use crate::path_utils::write_atomically; use anyhow::Context; use codex_config::CONFIG_TOML_FILE; +use codex_features::FEATURES; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ServiceTier; use codex_protocol::config_types::TrustLevel; diff --git a/codex-rs/core/src/config/managed_features.rs b/codex-rs/core/src/config/managed_features.rs index a8492d2d8..646a16153 100644 --- a/codex-rs/core/src/config/managed_features.rs +++ b/codex-rs/core/src/config/managed_features.rs @@ -10,11 +10,12 @@ use codex_config::Sourced; use crate::config::ConfigToml; use crate::config::profile::ConfigProfile; -use crate::features::Feature; -use crate::features::FeatureOverrides; -use crate::features::Features; -use crate::features::canonical_feature_for_key; -use crate::features::feature_for_key; +use codex_features::Feature; +use codex_features::FeatureConfigSource; +use codex_features::FeatureOverrides; +use codex_features::Features; +use codex_features::canonical_feature_for_key; +use codex_features::feature_for_key; /// Wrapper around [`Features`] which enforces constraints defined in /// `FeatureRequirementsToml` and provides normalization to ensure constraints @@ -304,7 +305,22 @@ pub(crate) fn validate_feature_requirements_in_config_toml( profile: &ConfigProfile, feature_requirements: Option<&Sourced>, ) -> std::io::Result<()> { - let configured_features = Features::from_config(cfg, profile, FeatureOverrides::default()); + let configured_features = Features::from_sources( + FeatureConfigSource { + features: cfg.features.as_ref(), + include_apply_patch_tool: None, + experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch, + experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool, + }, + FeatureConfigSource { + features: profile.features.as_ref(), + include_apply_patch_tool: profile.include_apply_patch_tool, + experimental_use_freeform_apply_patch: profile + .experimental_use_freeform_apply_patch, + experimental_use_unified_exec_tool: profile.experimental_use_unified_exec_tool, + }, + FeatureOverrides::default(), + ); ManagedFeatures::from_configured(configured_features, feature_requirements.cloned()) .map(|_| ()) .map_err(|err| { diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 965998d11..48bde3f17 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -39,10 +39,6 @@ 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::features::Feature; -use crate::features::FeatureOverrides; -use crate::features::Features; -use crate::features::FeaturesToml; use crate::git_info::resolve_root_git_project_for_trust; use crate::memories::memory_root; use crate::model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID; @@ -65,6 +61,11 @@ use crate::windows_sandbox::resolve_windows_sandbox_mode; use crate::windows_sandbox::resolve_windows_sandbox_private_desktop; use codex_app_server_protocol::Tools; use codex_app_server_protocol::UserSavedConfig; +use codex_features::Feature; +use codex_features::FeatureConfigSource; +use codex_features::FeatureOverrides; +use codex_features::Features; +use codex_features::FeaturesToml; use codex_protocol::config_types::AltScreenMode; use codex_protocol::config_types::ForcedLoginMethod; use codex_protocol::config_types::Personality; @@ -2189,7 +2190,23 @@ impl Config { web_search_request: override_tools_web_search_request, }; - let configured_features = Features::from_config(&cfg, &config_profile, feature_overrides); + let configured_features = Features::from_sources( + FeatureConfigSource { + features: cfg.features.as_ref(), + include_apply_patch_tool: None, + experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch, + experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool, + }, + FeatureConfigSource { + features: config_profile.features.as_ref(), + include_apply_patch_tool: config_profile.include_apply_patch_tool, + experimental_use_freeform_apply_patch: config_profile + .experimental_use_freeform_apply_patch, + experimental_use_unified_exec_tool: config_profile + .experimental_use_unified_exec_tool, + }, + feature_overrides, + ); let features = ManagedFeatures::from_configured(configured_features, feature_requirements)?; let windows_sandbox_mode = resolve_windows_sandbox_mode(&cfg, &config_profile); let windows_sandbox_private_desktop = diff --git a/codex-rs/core/src/config/profile.rs b/codex-rs/core/src/config/profile.rs index 743830ab3..e0947302e 100644 --- a/codex-rs/core/src/config/profile.rs +++ b/codex-rs/core/src/config/profile.rs @@ -8,6 +8,7 @@ use crate::config::types::ApprovalsReviewer; use crate::config::types::Personality; use crate::config::types::WindowsToml; use crate::protocol::AskForApproval; +use codex_features::FeaturesToml; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::SandboxMode; use codex_protocol::config_types::ServiceTier; @@ -60,7 +61,7 @@ pub struct ConfigProfile { #[serde(default)] // Injects known feature keys into the schema and forbids unknown keys. #[schemars(schema_with = "crate::config::schema::features_schema")] - pub features: Option, + pub features: Option, pub oss_provider: Option, } diff --git a/codex-rs/core/src/config/schema.rs b/codex-rs/core/src/config/schema.rs index 851f4d19e..102b7da51 100644 --- a/codex-rs/core/src/config/schema.rs +++ b/codex-rs/core/src/config/schema.rs @@ -1,6 +1,7 @@ use crate::config::ConfigToml; use crate::config::types::RawMcpServerConfig; -use crate::features::FEATURES; +use codex_features::FEATURES; +use codex_features::legacy_feature_keys; use schemars::r#gen::SchemaGenerator; use schemars::r#gen::SchemaSettings; use schemars::schema::InstanceType; @@ -25,7 +26,7 @@ pub(crate) fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema { .properties .insert(feature.key.to_string(), schema_gen.subschema_for::()); } - for legacy_key in crate::features::legacy_feature_keys() { + for legacy_key in legacy_feature_keys() { validation .properties .insert(legacy_key.to_string(), schema_gen.subschema_for::()); diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index fdd5cfb59..600ba9c6f 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -33,7 +33,6 @@ use crate::config_loader::AppsRequirementsToml; use crate::default_client::create_client; use crate::default_client::is_first_party_chat_originator; use crate::default_client::originator; -use crate::features::Feature; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; use crate::mcp::McpManager; use crate::mcp::ToolPluginProvenance; @@ -47,6 +46,7 @@ use crate::plugins::list_tool_suggest_discoverable_plugins; use crate::token_data::TokenData; use crate::tools::discoverable::DiscoverablePluginInfo; use crate::tools::discoverable::DiscoverableTool; +use codex_features::Feature; pub use codex_connectors::CONNECTORS_CACHE_TTL; const CONNECTORS_READY_TIMEOUT_ON_EMPTY_TOOLS: Duration = Duration::from_secs(30); diff --git a/codex-rs/core/src/connectors_tests.rs b/codex-rs/core/src/connectors_tests.rs index 5172db406..2a98621a8 100644 --- a/codex-rs/core/src/connectors_tests.rs +++ b/codex-rs/core/src/connectors_tests.rs @@ -11,9 +11,9 @@ use crate::config_loader::CloudRequirementsLoader; use crate::config_loader::ConfigLayerStack; use crate::config_loader::ConfigRequirements; use crate::config_loader::ConfigRequirementsToml; -use crate::features::Feature; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; use crate::mcp_connection_manager::ToolInfo; +use codex_features::Feature; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; use rmcp::model::JsonObject; diff --git a/codex-rs/core/src/context_manager/updates.rs b/codex-rs/core/src/context_manager/updates.rs index 031cfbe1f..871cf502a 100644 --- a/codex-rs/core/src/context_manager/updates.rs +++ b/codex-rs/core/src/context_manager/updates.rs @@ -1,9 +1,9 @@ use crate::codex::PreviousTurnSettings; use crate::codex::TurnContext; use crate::environment_context::EnvironmentContext; -use crate::features::Feature; use crate::shell::Shell; use codex_execpolicy::Policy; +use codex_features::Feature; use codex_protocol::config_types::Personality; use codex_protocol::models::ContentItem; use codex_protocol::models::DeveloperInstructions; diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index 59fa0107a..ea68fced6 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -30,10 +30,10 @@ use crate::config::ManagedFeatures; use crate::config::NetworkProxySpec; use crate::config::Permissions; use crate::config::types::McpServerConfig; -use crate::features::Feature; use crate::model_provider_info::ModelProviderInfo; use crate::protocol::SandboxPolicy; use crate::rollout::recorder::RolloutRecorder; +use codex_features::Feature; use super::GUARDIAN_REVIEW_TIMEOUT; use super::GUARDIAN_REVIEWER_NAME; diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index c02de978b..29436a0d7 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -39,7 +39,6 @@ pub mod exec; pub mod exec_env; mod exec_policy; pub mod external_agent_config; -pub mod features; mod file_watcher; mod flags; pub mod git_info; diff --git a/codex-rs/core/src/mcp/mod_tests.rs b/codex-rs/core/src/mcp/mod_tests.rs index 706f8ceb0..dc9465e10 100644 --- a/codex-rs/core/src/mcp/mod_tests.rs +++ b/codex-rs/core/src/mcp/mod_tests.rs @@ -1,9 +1,9 @@ use super::*; use crate::config::CONFIG_TOML_FILE; use crate::config::ConfigBuilder; -use crate::features::Feature; use crate::plugins::AppConnectorId; use crate::plugins::PluginCapabilitySummary; +use codex_features::Feature; use pretty_assertions::assert_eq; use std::fs; use std::path::Path; diff --git a/codex-rs/core/src/mcp/skill_dependencies.rs b/codex-rs/core/src/mcp/skill_dependencies.rs index 4e00c2eca..dc2dc360e 100644 --- a/codex-rs/core/src/mcp/skill_dependencies.rs +++ b/codex-rs/core/src/mcp/skill_dependencies.rs @@ -24,9 +24,9 @@ use crate::config::types::McpServerConfig; use crate::config::types::McpServerTransportConfig; use crate::default_client::is_first_party_originator; use crate::default_client::originator; -use crate::features::Feature; use crate::skills::SkillMetadata; use crate::skills::model::SkillToolDependency; +use codex_features::Feature; const SKILL_MCP_DEPENDENCY_PROMPT_ID: &str = "skill_mcp_dependency_install"; const MCP_DEPENDENCY_OPTION_INSTALL: &str = "Install"; diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 16a05df95..3e7f0cb84 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -19,7 +19,6 @@ use crate::config::edit::ConfigEdit; use crate::config::edit::ConfigEditsBuilder; use crate::config::types::AppToolApproval; use crate::connectors; -use crate::features::Feature; use crate::guardian::GuardianApprovalRequest; use crate::guardian::GuardianMcpAnnotations; use crate::guardian::guardian_approval_request_to_json; @@ -33,6 +32,7 @@ use crate::protocol::McpInvocation; use crate::protocol::McpToolCallBeginEvent; use crate::protocol::McpToolCallEndEvent; use crate::state_db; +use codex_features::Feature; use codex_protocol::mcp::CallToolResult; use codex_protocol::openai_models::InputModality; use codex_protocol::protocol::AskForApproval; diff --git a/codex-rs/core/src/memories/phase2.rs b/codex-rs/core/src/memories/phase2.rs index 6eb10edb7..2e0d7c4ad 100644 --- a/codex-rs/core/src/memories/phase2.rs +++ b/codex-rs/core/src/memories/phase2.rs @@ -2,7 +2,6 @@ use crate::agent::AgentStatus; use crate::agent::status::is_final as is_final_agent_status; use crate::codex::Session; use crate::config::Config; -use crate::features::Feature; use crate::memories::memory_root; use crate::memories::metrics; use crate::memories::phase_two; @@ -11,6 +10,7 @@ use crate::memories::storage::rebuild_raw_memories_file_from_memories; use crate::memories::storage::rollout_summary_file_stem; use crate::memories::storage::sync_rollout_summaries_from_memories; use codex_config::Constrained; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; diff --git a/codex-rs/core/src/memories/start.rs b/codex-rs/core/src/memories/start.rs index 8059dd0eb..f9d6802be 100644 --- a/codex-rs/core/src/memories/start.rs +++ b/codex-rs/core/src/memories/start.rs @@ -1,8 +1,8 @@ use crate::codex::Session; use crate::config::Config; -use crate::features::Feature; use crate::memories::phase1; use crate::memories::phase2; +use codex_features::Feature; use codex_protocol::protocol::SessionSource; use std::sync::Arc; use tracing::warn; diff --git a/codex-rs/core/src/models_manager/model_info.rs b/codex-rs/core/src/models_manager/model_info.rs index d4f82b223..7d3c6e9d1 100644 --- a/codex-rs/core/src/models_manager/model_info.rs +++ b/codex-rs/core/src/models_manager/model_info.rs @@ -10,8 +10,8 @@ use codex_protocol::openai_models::WebSearchToolType; use codex_protocol::openai_models::default_input_modalities; use crate::config::Config; -use crate::features::Feature; use crate::truncate::approx_bytes_for_tokens; +use codex_features::Feature; use tracing::warn; pub const BASE_INSTRUCTIONS: &str = include_str!("../../prompt.md"); diff --git a/codex-rs/core/src/original_image_detail.rs b/codex-rs/core/src/original_image_detail.rs index d5bb6d24c..8db219f12 100644 --- a/codex-rs/core/src/original_image_detail.rs +++ b/codex-rs/core/src/original_image_detail.rs @@ -1,5 +1,5 @@ -use crate::features::Feature; -use crate::features::Features; +use codex_features::Feature; +use codex_features::Features; use codex_protocol::models::ImageDetail; use codex_protocol::openai_models::ModelInfo; diff --git a/codex-rs/core/src/original_image_detail_tests.rs b/codex-rs/core/src/original_image_detail_tests.rs index b771e87bb..e4a3c0988 100644 --- a/codex-rs/core/src/original_image_detail_tests.rs +++ b/codex-rs/core/src/original_image_detail_tests.rs @@ -1,8 +1,8 @@ use super::*; use crate::config::test_config; -use crate::features::Features; use crate::models_manager::manager::ModelsManager; +use codex_features::Features; use pretty_assertions::assert_eq; #[test] diff --git a/codex-rs/core/src/otel_init.rs b/codex-rs/core/src/otel_init.rs index 74e30ef82..0bec06724 100644 --- a/codex-rs/core/src/otel_init.rs +++ b/codex-rs/core/src/otel_init.rs @@ -2,7 +2,7 @@ use crate::config::Config; use crate::config::types::OtelExporterKind as Kind; use crate::config::types::OtelHttpProtocol as Protocol; use crate::default_client::originator; -use crate::features::Feature; +use codex_features::Feature; use codex_otel::OtelProvider; use codex_otel::config::OtelExporter; use codex_otel::config::OtelHttpProtocol; diff --git a/codex-rs/core/src/plugins/discoverable.rs b/codex-rs/core/src/plugins/discoverable.rs index 0de3ac1c1..5d054c2ff 100644 --- a/codex-rs/core/src/plugins/discoverable.rs +++ b/codex-rs/core/src/plugins/discoverable.rs @@ -8,7 +8,7 @@ use super::PluginReadRequest; use super::PluginsManager; use crate::config::Config; use crate::config::types::ToolSuggestDiscoverableType; -use crate::features::Feature; +use codex_features::Feature; const TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST: &[&str] = &[ "github@openai-curated", diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index aabe77812..6f00bf5c7 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -36,12 +36,12 @@ use crate::config::edit::ConfigEditsBuilder; use crate::config::types::McpServerConfig; use crate::config::types::PluginConfig; use crate::config_loader::ConfigLayerStack; -use crate::features::Feature; use crate::skills::SkillMetadata; use crate::skills::loader::SkillRoot; use crate::skills::loader::load_skills_from_roots; use codex_app_server_protocol::ConfigValueWriteParams; use codex_app_server_protocol::MergeStrategy; +use codex_features::Feature; use codex_protocol::protocol::Product; use codex_protocol::protocol::SkillScope; use codex_utils_absolute_path::AbsolutePathBuf; diff --git a/codex-rs/core/src/project_doc.rs b/codex-rs/core/src/project_doc.rs index aa6c3b3e7..7ad122c40 100644 --- a/codex-rs/core/src/project_doc.rs +++ b/codex-rs/core/src/project_doc.rs @@ -20,8 +20,8 @@ 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 crate::features::Feature; use codex_app_server_protocol::ConfigLayerSource; +use codex_features::Feature; use dunce::canonicalize as normalize_path; use std::path::PathBuf; use tokio::io::AsyncReadExt; diff --git a/codex-rs/core/src/project_doc_tests.rs b/codex-rs/core/src/project_doc_tests.rs index 1b7f5b900..4cea541be 100644 --- a/codex-rs/core/src/project_doc_tests.rs +++ b/codex-rs/core/src/project_doc_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::config::ConfigBuilder; -use crate::features::Feature; +use codex_features::Feature; use std::fs; use std::path::PathBuf; use tempfile::TempDir; diff --git a/codex-rs/core/src/rollout/recorder_tests.rs b/codex-rs/core/src/rollout/recorder_tests.rs index dbe11ac9f..8ca7b58a6 100644 --- a/codex-rs/core/src/rollout/recorder_tests.rs +++ b/codex-rs/core/src/rollout/recorder_tests.rs @@ -1,7 +1,7 @@ use super::*; use crate::config::ConfigBuilder; -use crate::features::Feature; use chrono::TimeZone; +use codex_features::Feature; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; use codex_protocol::protocol::AgentMessageEvent; use codex_protocol::protocol::AskForApproval; diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index c52e4f917..b8e1d73b7 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -47,7 +47,7 @@ use codex_protocol::models::ResponseItem; use codex_protocol::protocol::RolloutItem; use codex_protocol::user_input::UserInput; -use crate::features::Feature; +use codex_features::Feature; pub(crate) use compact::CompactTask; pub(crate) use ghost_snapshot::GhostSnapshotTask; pub(crate) use regular::RegularTask; diff --git a/codex-rs/core/src/tasks/review.rs b/codex-rs/core/src/tasks/review.rs index 67e398edb..bdcb155a3 100644 --- a/codex-rs/core/src/tasks/review.rs +++ b/codex-rs/core/src/tasks/review.rs @@ -20,10 +20,10 @@ use crate::codex::Session; use crate::codex::TurnContext; use crate::codex_delegate::run_codex_thread_one_shot; use crate::config::Constrained; -use crate::features::Feature; use crate::review_format::format_review_findings_block; use crate::review_format::render_review_output_text; use crate::state::TaskKind; +use codex_features::Feature; use codex_protocol::user_input::UserInput; use super::SessionTask; diff --git a/codex-rs/core/src/tools/code_mode/service.rs b/codex-rs/core/src/tools/code_mode/service.rs index 52b519651..a9fadedb8 100644 --- a/codex-rs/core/src/tools/code_mode/service.rs +++ b/codex-rs/core/src/tools/code_mode/service.rs @@ -8,11 +8,11 @@ use tracing::warn; use crate::codex::Session; use crate::codex::TurnContext; -use crate::features::Feature; use crate::tools::ToolRouter; use crate::tools::context::SharedTurnDiffTracker; use crate::tools::js_repl::resolve_compatible_node; use crate::tools::parallel::ToolCallRuntime; +use codex_features::Feature; use super::ExecContext; use super::PUBLIC_TOOL_NAME; diff --git a/codex-rs/core/src/tools/handlers/artifacts.rs b/codex-rs/core/src/tools/handlers/artifacts.rs index 1431de0e2..875fcd486 100644 --- a/codex-rs/core/src/tools/handlers/artifacts.rs +++ b/codex-rs/core/src/tools/handlers/artifacts.rs @@ -13,7 +13,6 @@ use crate::codex::Session; use crate::codex::TurnContext; use crate::exec::ExecToolCallOutput; use crate::exec::StreamOutput; -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::packages::versions; use crate::protocol::ExecCommandSource; @@ -26,6 +25,7 @@ use crate::tools::events::ToolEventFailure; use crate::tools::events::ToolEventStage; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; +use codex_features::Feature; const ARTIFACTS_TOOL_NAME: &str = "artifacts"; const ARTIFACT_TOOL_PRAGMA_PREFIX: &str = "// codex-artifact-tool:"; diff --git a/codex-rs/core/src/tools/handlers/js_repl.rs b/codex-rs/core/src/tools/handlers/js_repl.rs index 38d0d388e..b380a7107 100644 --- a/codex-rs/core/src/tools/handlers/js_repl.rs +++ b/codex-rs/core/src/tools/handlers/js_repl.rs @@ -6,7 +6,6 @@ use std::time::Instant; use crate::exec::ExecToolCallOutput; use crate::exec::StreamOutput; -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::protocol::ExecCommandSource; use crate::tools::context::FunctionToolOutput; @@ -21,6 +20,7 @@ use crate::tools::js_repl::JS_REPL_PRAGMA_PREFIX; use crate::tools::js_repl::JsReplArgs; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; +use codex_features::Feature; use codex_protocol::models::FunctionCallOutputContentItem; pub struct JsReplHandler; diff --git a/codex-rs/core/src/tools/handlers/multi_agents.rs b/codex-rs/core/src/tools/handlers/multi_agents.rs index 75ce10378..8fa990a3b 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents.rs @@ -11,7 +11,6 @@ use crate::codex::Session; use crate::codex::TurnContext; use crate::config::Config; use crate::error::CodexErr; -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::models_manager::manager::RefreshStrategy; use crate::tools::context::FunctionToolOutput; @@ -22,6 +21,7 @@ use crate::tools::handlers::parse_arguments; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; use async_trait::async_trait; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::models::BaseInstructions; use codex_protocol::models::ResponseInputItem; 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 be34a1570..abd491efd 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -6,7 +6,6 @@ use crate::built_in_model_providers; use crate::codex::make_session_and_context; use crate::config::DEFAULT_AGENT_MAX_DEPTH; use crate::config::types::ShellEnvironmentPolicy; -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::protocol::AskForApproval; use crate::protocol::FileSystemSandboxPolicy; @@ -17,6 +16,7 @@ use crate::protocol::SessionSource; use crate::protocol::SubAgentSource; use crate::tools::context::ToolOutput; use crate::turn_diff_tracker::TurnDiffTracker; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::models::ContentItem; use codex_protocol::models::FunctionCallOutputBody; diff --git a/codex-rs/core/src/tools/handlers/shell.rs b/codex-rs/core/src/tools/handlers/shell.rs index b0f14fdd4..446ca100b 100644 --- a/codex-rs/core/src/tools/handlers/shell.rs +++ b/codex-rs/core/src/tools/handlers/shell.rs @@ -9,7 +9,6 @@ use crate::exec::ExecCapturePolicy; use crate::exec::ExecParams; use crate::exec_env::create_env; use crate::exec_policy::ExecApprovalRequest; -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::is_safe_command::is_known_safe_command; use crate::protocol::ExecCommandSource; @@ -34,6 +33,7 @@ use crate::tools::runtimes::shell::ShellRuntime; use crate::tools::runtimes::shell::ShellRuntimeBackend; use crate::tools::sandboxing::ToolCtx; use crate::tools::spec::ShellCommandBackendConfig; +use codex_features::Feature; use codex_protocol::models::PermissionProfile; pub struct ShellHandler; diff --git a/codex-rs/core/src/tools/handlers/unified_exec.rs b/codex-rs/core/src/tools/handlers/unified_exec.rs index c79edf305..109ac713c 100644 --- a/codex-rs/core/src/tools/handlers/unified_exec.rs +++ b/codex-rs/core/src/tools/handlers/unified_exec.rs @@ -1,4 +1,3 @@ -use crate::features::Feature; use crate::function_tool::FunctionCallError; use crate::is_safe_command::is_known_safe_command; use crate::protocol::EventMsg; @@ -25,6 +24,7 @@ use crate::unified_exec::UnifiedExecContext; use crate::unified_exec::UnifiedExecProcessManager; use crate::unified_exec::WriteStdinRequest; use async_trait::async_trait; +use codex_features::Feature; use codex_protocol::models::PermissionProfile; use serde::Deserialize; use std::path::PathBuf; diff --git a/codex-rs/core/src/tools/js_repl/mod_tests.rs b/codex-rs/core/src/tools/js_repl/mod_tests.rs index 54779d809..413e171d4 100644 --- a/codex-rs/core/src/tools/js_repl/mod_tests.rs +++ b/codex-rs/core/src/tools/js_repl/mod_tests.rs @@ -1,11 +1,11 @@ use super::*; use crate::codex::make_session_and_context; use crate::codex::make_session_and_context_with_dynamic_tools_and_rx; -use crate::features::Feature; use crate::protocol::AskForApproval; use crate::protocol::EventMsg; use crate::protocol::SandboxPolicy; use crate::turn_diff_tracker::TurnDiffTracker; +use codex_features::Feature; use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem; use codex_protocol::dynamic_tools::DynamicToolResponse; use codex_protocol::dynamic_tools::DynamicToolSpec; diff --git a/codex-rs/core/src/tools/runtimes/shell.rs b/codex-rs/core/src/tools/runtimes/shell.rs index d7b07ed0d..18afb20bc 100644 --- a/codex-rs/core/src/tools/runtimes/shell.rs +++ b/codex-rs/core/src/tools/runtimes/shell.rs @@ -10,7 +10,6 @@ pub(crate) mod zsh_fork_backend; use crate::command_canonicalization::canonicalize_command_for_approval; use crate::exec::ExecToolCallOutput; -use crate::features::Feature; use crate::guardian::GuardianApprovalRequest; use crate::guardian::review_approval_request; use crate::guardian::routes_approval_to_guardian; @@ -34,6 +33,7 @@ use crate::tools::sandboxing::ToolError; use crate::tools::sandboxing::ToolRuntime; use crate::tools::sandboxing::sandbox_override_for_first_attempt; use crate::tools::sandboxing::with_cached_approval; +use codex_features::Feature; use codex_network_proxy::NetworkProxy; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::ReviewDecision; diff --git a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs index 76c711bfd..948018dae 100644 --- a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs +++ b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs @@ -6,7 +6,6 @@ use crate::exec::ExecExpiration; use crate::exec::ExecToolCallOutput; use crate::exec::SandboxType; use crate::exec::is_likely_sandbox_denied; -use crate::features::Feature; use crate::guardian::GuardianApprovalRequest; use crate::guardian::review_approval_request; use crate::guardian::routes_approval_to_guardian; @@ -25,6 +24,7 @@ use codex_execpolicy::Evaluation; use codex_execpolicy::MatchOptions; use codex_execpolicy::Policy; use codex_execpolicy::RuleMatch; +use codex_features::Feature; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::MacOsSeatbeltProfileExtensions; use codex_protocol::models::PermissionProfile; diff --git a/codex-rs/core/src/tools/runtimes/unified_exec.rs b/codex-rs/core/src/tools/runtimes/unified_exec.rs index 22fc732f6..0b6409215 100644 --- a/codex-rs/core/src/tools/runtimes/unified_exec.rs +++ b/codex-rs/core/src/tools/runtimes/unified_exec.rs @@ -8,7 +8,6 @@ use crate::command_canonicalization::canonicalize_command_for_approval; use crate::error::CodexErr; use crate::error::SandboxErr; use crate::exec::ExecExpiration; -use crate::features::Feature; use crate::guardian::GuardianApprovalRequest; use crate::guardian::review_approval_request; use crate::guardian::routes_approval_to_guardian; @@ -37,6 +36,7 @@ use crate::unified_exec::NoopSpawnLifecycle; use crate::unified_exec::UnifiedExecError; use crate::unified_exec::UnifiedExecProcess; use crate::unified_exec::UnifiedExecProcessManager; +use codex_features::Feature; use codex_network_proxy::NetworkProxy; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::ReviewDecision; diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index a91e95bbe..662e97d10 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -3,8 +3,6 @@ use crate::client_common::tools::FreeformToolFormat; use crate::client_common::tools::ResponsesApiTool; use crate::client_common::tools::ToolSpec; use crate::config::AgentRoleConfig; -use crate::features::Feature; -use crate::features::Features; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; use crate::mcp_connection_manager::ToolInfo; use crate::models_manager::collaboration_mode_presets::CollaborationModesConfig; @@ -35,6 +33,8 @@ use crate::tools::handlers::request_permissions_tool_description; use crate::tools::handlers::request_user_input_tool_description; use crate::tools::registry::ToolRegistryBuilder; use crate::tools::registry::tool_handler_key; +use codex_features::Feature; +use codex_features::Features; use codex_protocol::config_types::WebSearchConfig; use codex_protocol::config_types::WebSearchMode; use codex_protocol::config_types::WindowsSandboxLevel; diff --git a/codex-rs/core/src/windows_sandbox.rs b/codex-rs/core/src/windows_sandbox.rs index 79f5c2f1e..312c4ebbe 100644 --- a/codex-rs/core/src/windows_sandbox.rs +++ b/codex-rs/core/src/windows_sandbox.rs @@ -4,10 +4,10 @@ use crate::config::edit::ConfigEditsBuilder; use crate::config::profile::ConfigProfile; use crate::config::types::WindowsSandboxModeToml; use crate::default_client::originator; -use crate::features::Feature; -use crate::features::Features; -use crate::features::FeaturesToml; use crate::protocol::SandboxPolicy; +use codex_features::Feature; +use codex_features::Features; +use codex_features::FeaturesToml; use codex_otel::sanitize_metric_tag_value; use codex_protocol::config_types::WindowsSandboxLevel; use std::collections::BTreeMap; diff --git a/codex-rs/core/src/windows_sandbox_tests.rs b/codex-rs/core/src/windows_sandbox_tests.rs index a7506e7de..cc41dfa4c 100644 --- a/codex-rs/core/src/windows_sandbox_tests.rs +++ b/codex-rs/core/src/windows_sandbox_tests.rs @@ -1,7 +1,7 @@ use super::*; use crate::config::types::WindowsToml; -use crate::features::Features; -use crate::features::FeaturesToml; +use codex_features::Features; +use codex_features::FeaturesToml; use pretty_assertions::assert_eq; use std::collections::BTreeMap; diff --git a/codex-rs/core/tests/common/Cargo.toml b/codex-rs/core/tests/common/Cargo.toml index 86ecf2921..7377e40f5 100644 --- a/codex-rs/core/tests/common/Cargo.toml +++ b/codex-rs/core/tests/common/Cargo.toml @@ -11,7 +11,9 @@ path = "lib.rs" anyhow = { workspace = true } assert_cmd = { workspace = true } base64 = { workspace = true } +codex-arg0 = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-protocol = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-cargo-bin = { workspace = true } diff --git a/codex-rs/core/tests/common/lib.rs b/codex-rs/core/tests/common/lib.rs index 17f949beb..6209ded40 100644 --- a/codex-rs/core/tests/common/lib.rs +++ b/codex-rs/core/tests/common/lib.rs @@ -2,8 +2,10 @@ use anyhow::Context as _; use anyhow::ensure; +use codex_arg0::Arg0PathEntryGuard; use codex_utils_cargo_bin::CargoBinError; use ctor::ctor; +use std::sync::OnceLock; use tempfile::TempDir; use codex_core::CodexThread; @@ -24,12 +26,19 @@ pub mod test_codex_exec; pub mod tracing; pub mod zsh_fork; +static TEST_ARG0_PATH_ENTRY: OnceLock> = OnceLock::new(); + #[ctor] fn enable_deterministic_unified_exec_process_ids_for_tests() { codex_core::test_support::set_thread_manager_test_mode(/*enabled*/ true); codex_core::test_support::set_deterministic_process_ids(/*enabled*/ true); } +#[ctor] +fn configure_arg0_dispatch_for_test_binaries() { + let _ = TEST_ARG0_PATH_ENTRY.get_or_init(codex_arg0::arg0_dispatch); +} + #[ctor] fn configure_insta_workspace_root_for_snapshot_tests() { if std::env::var_os("INSTA_WORKSPACE_ROOT").is_some() { @@ -155,8 +164,7 @@ pub async fn load_default_config_for_test(codex_home: &TempDir) -> Config { fn default_test_overrides() -> ConfigOverrides { ConfigOverrides { codex_linux_sandbox_exe: Some( - codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox") - .expect("should find binary for codex-linux-sandbox"), + find_codex_linux_sandbox_exe().expect("should find binary for codex-linux-sandbox"), ), ..ConfigOverrides::default() } @@ -167,6 +175,23 @@ fn default_test_overrides() -> ConfigOverrides { ConfigOverrides::default() } +#[cfg(target_os = "linux")] +pub fn find_codex_linux_sandbox_exe() -> Result { + if let Ok(path) = std::env::current_exe() { + return Ok(path); + } + + if let Some(path) = TEST_ARG0_PATH_ENTRY + .get() + .and_then(Option::as_ref) + .and_then(|path_entry| path_entry.paths().codex_linux_sandbox_exe.clone()) + { + return Ok(path); + } + + codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox") +} + /// Builds an SSE stream body from a JSON fixture. /// /// The fixture must contain an array of objects where each object represents a @@ -482,7 +507,7 @@ macro_rules! codex_linux_sandbox_exe_or_skip { () => {{ #[cfg(target_os = "linux")] { - match codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox") { + match $crate::find_codex_linux_sandbox_exe() { Ok(path) => Some(path), Err(err) => { eprintln!("codex-linux-sandbox binary not available, skipping test: {err}"); @@ -498,7 +523,7 @@ macro_rules! codex_linux_sandbox_exe_or_skip { ($return_value:expr $(,)?) => {{ #[cfg(target_os = "linux")] { - match codex_utils_cargo_bin::cargo_bin("codex-linux-sandbox") { + match $crate::find_codex_linux_sandbox_exe() { Ok(path) => Some(path), Err(err) => { eprintln!("codex-linux-sandbox binary not available, skipping test: {err}"); diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 860b99468..116a30d28 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -11,10 +11,10 @@ use codex_core::ModelProviderInfo; use codex_core::ThreadManager; use codex_core::built_in_model_providers; use codex_core::config::Config; -use codex_core::features::Feature; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_core::shell::Shell; use codex_core::shell::get_shell_by_model_provided_path; +use codex_features::Feature; use codex_protocol::config_types::ServiceTier; use codex_protocol::openai_models::ModelsResponse; use codex_protocol::protocol::AskForApproval; diff --git a/codex-rs/core/tests/common/zsh_fork.rs b/codex-rs/core/tests/common/zsh_fork.rs index ff9509699..e61d3ea95 100644 --- a/codex-rs/core/tests/common/zsh_fork.rs +++ b/codex-rs/core/tests/common/zsh_fork.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use anyhow::Result; use codex_core::config::Config; use codex_core::config::Constrained; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; diff --git a/codex-rs/core/tests/suite/agent_jobs.rs b/codex-rs/core/tests/suite/agent_jobs.rs index 443043c6f..75204a865 100644 --- a/codex-rs/core/tests/suite/agent_jobs.rs +++ b/codex-rs/core/tests/suite/agent_jobs.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_function_call; use core_test_support::responses::ev_response_created; diff --git a/codex-rs/core/tests/suite/agent_websocket.rs b/codex-rs/core/tests/suite/agent_websocket.rs index 6b38ca2b4..5c1c5bd07 100644 --- a/codex-rs/core/tests/suite/agent_websocket.rs +++ b/codex-rs/core/tests/suite/agent_websocket.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::ServiceTier; use core_test_support::responses::WebSocketConnectionConfig; use core_test_support::responses::ev_assistant_message; diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index f5390a411..b113fc465 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -13,7 +13,7 @@ use std::fs; use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; diff --git a/codex-rs/core/tests/suite/approvals.rs b/codex-rs/core/tests/suite/approvals.rs index 49b9ac59d..f77697d01 100644 --- a/codex-rs/core/tests/suite/approvals.rs +++ b/codex-rs/core/tests/suite/approvals.rs @@ -9,8 +9,8 @@ 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::features::Feature; use codex_core::sandboxing::SandboxPermissions; +use codex_features::Feature; use codex_protocol::approvals::NetworkApprovalProtocol; use codex_protocol::approvals::NetworkPolicyAmendment; use codex_protocol::approvals::NetworkPolicyRuleAction; diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 35dee3fa7..3ea30c596 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -10,8 +10,8 @@ use codex_core::auth::AuthCredentialsStoreMode; use codex_core::built_in_model_providers; use codex_core::default_client::originator; use codex_core::error::CodexErr; -use codex_core::features::Feature; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; +use codex_features::Feature; use codex_otel::SessionTelemetry; use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; diff --git a/codex-rs/core/tests/suite/client_websockets.rs b/codex-rs/core/tests/suite/client_websockets.rs index 4416ff108..b568c6aee 100755 --- a/codex-rs/core/tests/suite/client_websockets.rs +++ b/codex-rs/core/tests/suite/client_websockets.rs @@ -9,7 +9,7 @@ use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::WireApi; use codex_core::X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER; -use codex_core::features::Feature; +use codex_features::Feature; use codex_otel::SessionTelemetry; use codex_otel::TelemetryAuthMode; use codex_otel::current_span_w3c_trace_context; diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 8d80f3a5c..941249cca 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -5,7 +5,7 @@ use base64::Engine; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerTransportConfig; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem; use codex_protocol::dynamic_tools::DynamicToolResponse; use codex_protocol::dynamic_tools::DynamicToolSpec; diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index f02ab6574..2f4365a95 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -5,6 +5,7 @@ use codex_core::built_in_model_providers; use codex_core::compact::SUMMARIZATION_PROMPT; use codex_core::compact::SUMMARY_PREFIX; use codex_core::config::Config; +use codex_features::Feature; use codex_protocol::items::TurnItem; use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ModelsResponse; @@ -3115,9 +3116,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch .with_config(move |config| { config.model_provider = model_provider; set_test_compact_prompt(config); - let _ = config - .features - .enable(codex_core::features::Feature::RemoteModels); + let _ = config.features.enable(Feature::RemoteModels); config.model_auto_compact_token_limit = Some(200); }) .build(&server) diff --git a/codex-rs/core/tests/suite/deprecation_notice.rs b/codex-rs/core/tests/suite/deprecation_notice.rs index c260af6d6..26a56c86e 100644 --- a/codex-rs/core/tests/suite/deprecation_notice.rs +++ b/codex-rs/core/tests/suite/deprecation_notice.rs @@ -6,7 +6,7 @@ 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_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::DeprecationNoticeEvent; use codex_protocol::protocol::EventMsg; use core_test_support::responses::start_mock_server; diff --git a/codex-rs/core/tests/suite/exec_policy.rs b/codex-rs/core/tests/suite/exec_policy.rs index 5b34b20c7..18be46802 100644 --- a/codex-rs/core/tests/suite/exec_policy.rs +++ b/codex-rs/core/tests/suite/exec_policy.rs @@ -1,7 +1,7 @@ #![allow(clippy::unwrap_used, clippy::expect_used)] use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Settings; diff --git a/codex-rs/core/tests/suite/hierarchical_agents.rs b/codex-rs/core/tests/suite/hierarchical_agents.rs index 9eb901595..e1c45d641 100644 --- a/codex-rs/core/tests/suite/hierarchical_agents.rs +++ b/codex-rs/core/tests/suite/hierarchical_agents.rs @@ -1,4 +1,4 @@ -use codex_core::features::Feature; +use codex_features::Feature; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index 0334db8b4..c7042dd25 100644 --- a/codex-rs/core/tests/suite/hooks.rs +++ b/codex-rs/core/tests/suite/hooks.rs @@ -3,7 +3,7 @@ use std::path::Path; use anyhow::Context; use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::items::parse_hook_prompt_fragment; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; diff --git a/codex-rs/core/tests/suite/js_repl.rs b/codex-rs/core/tests/suite/js_repl.rs index 4ebfb52cb..5f52c24e1 100644 --- a/codex-rs/core/tests/suite/js_repl.rs +++ b/codex-rs/core/tests/suite/js_repl.rs @@ -1,7 +1,7 @@ #![allow(clippy::expect_used, clippy::unwrap_used)] use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::EventMsg; use core_test_support::responses; use core_test_support::responses::ResponseMock; diff --git a/codex-rs/core/tests/suite/memories.rs b/codex-rs/core/tests/suite/memories.rs index df7ffafb2..c4e97f964 100644 --- a/codex-rs/core/tests/suite/memories.rs +++ b/codex-rs/core/tests/suite/memories.rs @@ -1,7 +1,7 @@ use anyhow::Result; use chrono::Duration as ChronoDuration; use chrono::Utc; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; diff --git a/codex-rs/core/tests/suite/model_switching.rs b/codex-rs/core/tests/suite/model_switching.rs index c6e21c9ee..9902f0ee6 100644 --- a/codex-rs/core/tests/suite/model_switching.rs +++ b/codex-rs/core/tests/suite/model_switching.rs @@ -1,8 +1,8 @@ use anyhow::Result; use codex_core::CodexAuth; use codex_core::config::types::Personality; -use codex_core::features::Feature; use codex_core::models_manager::manager::RefreshStrategy; +use codex_features::Feature; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::ServiceTier; use codex_protocol::openai_models::ConfigShellToolType; diff --git a/codex-rs/core/tests/suite/model_visible_layout.rs b/codex-rs/core/tests/suite/model_visible_layout.rs index 587436c83..a10fa7c26 100644 --- a/codex-rs/core/tests/suite/model_visible_layout.rs +++ b/codex-rs/core/tests/suite/model_visible_layout.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use anyhow::Result; use codex_core::config::types::Personality; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; diff --git a/codex-rs/core/tests/suite/otel.rs b/codex-rs/core/tests/suite/otel.rs index c96988d18..ecf628366 100644 --- a/codex-rs/core/tests/suite/otel.rs +++ b/codex-rs/core/tests/suite/otel.rs @@ -1,5 +1,5 @@ use codex_core::config::Constrained; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index 600b6490a..9a495e7af 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -1,7 +1,7 @@ use codex_core::config::types::Personality; -use codex_core::features::Feature; use codex_core::models_manager::manager::ModelsManager; use codex_core::models_manager::manager::RefreshStrategy; +use codex_features::Feature; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::ModelInfo; diff --git a/codex-rs/core/tests/suite/plugins.rs b/codex-rs/core/tests/suite/plugins.rs index 0eba6e323..78df34652 100644 --- a/codex-rs/core/tests/suite/plugins.rs +++ b/codex-rs/core/tests/suite/plugins.rs @@ -7,7 +7,7 @@ use std::time::Instant; use anyhow::Result; use codex_core::CodexAuth; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; use core_test_support::apps_test_server::AppsTestServer; diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 96b7f6457..14caaf8f0 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -1,9 +1,9 @@ #![allow(clippy::unwrap_used)] use codex_apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS; -use codex_core::features::Feature; use codex_core::shell::Shell; use codex_core::shell::default_user_shell; +use codex_features::Feature; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::ReasoningSummary; diff --git a/codex-rs/core/tests/suite/request_compression.rs b/codex-rs/core/tests/suite/request_compression.rs index 7f8b996c0..dd4249928 100644 --- a/codex-rs/core/tests/suite/request_compression.rs +++ b/codex-rs/core/tests/suite/request_compression.rs @@ -1,7 +1,7 @@ #![cfg(not(target_os = "windows"))] use codex_core::CodexAuth; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; use codex_protocol::user_input::UserInput; diff --git a/codex-rs/core/tests/suite/request_permissions.rs b/codex-rs/core/tests/suite/request_permissions.rs index 9373a938a..b1aaac65b 100644 --- a/codex-rs/core/tests/suite/request_permissions.rs +++ b/codex-rs/core/tests/suite/request_permissions.rs @@ -2,8 +2,8 @@ use anyhow::Result; use codex_core::config::Constrained; -use codex_core::features::Feature; use codex_core::sandboxing::SandboxPermissions; +use codex_features::Feature; use codex_protocol::models::FileSystemPermissions; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::AskForApproval; diff --git a/codex-rs/core/tests/suite/request_permissions_tool.rs b/codex-rs/core/tests/suite/request_permissions_tool.rs index 8a092f69f..a01d6e0ab 100644 --- a/codex-rs/core/tests/suite/request_permissions_tool.rs +++ b/codex-rs/core/tests/suite/request_permissions_tool.rs @@ -3,7 +3,7 @@ use anyhow::Result; use codex_core::config::Constrained; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::models::FileSystemPermissions; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/request_user_input.rs b/codex-rs/core/tests/suite/request_user_input.rs index f66c2f209..1bf759d27 100644 --- a/codex-rs/core/tests/suite/request_user_input.rs +++ b/codex-rs/core/tests/suite/request_user_input.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Settings; diff --git a/codex-rs/core/tests/suite/search_tool.rs b/codex-rs/core/tests/suite/search_tool.rs index dd182befb..0eee2f1d3 100644 --- a/codex-rs/core/tests/suite/search_tool.rs +++ b/codex-rs/core/tests/suite/search_tool.rs @@ -4,7 +4,7 @@ use anyhow::Result; use codex_core::CodexAuth; use codex_core::config::Config; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::openai_models::ModelsResponse; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/shell_command.rs b/codex-rs/core/tests/suite/shell_command.rs index cbb10e768..9a128b6a8 100644 --- a/codex-rs/core/tests/suite/shell_command.rs +++ b/codex-rs/core/tests/suite/shell_command.rs @@ -1,7 +1,7 @@ use std::time::Duration; use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use core_test_support::assert_regex_match; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; diff --git a/codex-rs/core/tests/suite/shell_snapshot.rs b/codex-rs/core/tests/suite/shell_snapshot.rs index 491853f27..68228a412 100644 --- a/codex-rs/core/tests/suite/shell_snapshot.rs +++ b/codex-rs/core/tests/suite/shell_snapshot.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ExecCommandBeginEvent; diff --git a/codex-rs/core/tests/suite/spawn_agent_description.rs b/codex-rs/core/tests/suite/spawn_agent_description.rs index df2d49a93..dfc5a2e54 100644 --- a/codex-rs/core/tests/suite/spawn_agent_description.rs +++ b/codex-rs/core/tests/suite/spawn_agent_description.rs @@ -3,9 +3,9 @@ use anyhow::Result; use codex_core::CodexAuth; -use codex_core::features::Feature; use codex_core::models_manager::manager::ModelsManager; use codex_core::models_manager::manager::RefreshStrategy; +use codex_features::Feature; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::ModelInfo; diff --git a/codex-rs/core/tests/suite/sqlite_state.rs b/codex-rs/core/tests/suite/sqlite_state.rs index 620b9b508..2801f1ab1 100644 --- a/codex-rs/core/tests/suite/sqlite_state.rs +++ b/codex-rs/core/tests/suite/sqlite_state.rs @@ -1,7 +1,7 @@ use anyhow::Result; use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerTransportConfig; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::dynamic_tools::DynamicToolSpec; use codex_protocol::protocol::AskForApproval; diff --git a/codex-rs/core/tests/suite/subagent_notifications.rs b/codex-rs/core/tests/suite/subagent_notifications.rs index 895997579..33abc6c7a 100644 --- a/codex-rs/core/tests/suite/subagent_notifications.rs +++ b/codex-rs/core/tests/suite/subagent_notifications.rs @@ -1,7 +1,7 @@ use anyhow::Result; use codex_core::ThreadConfigSnapshot; use codex_core::config::AgentRoleConfig; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::openai_models::ReasoningEffort; use core_test_support::responses::ResponsesRequest; diff --git a/codex-rs/core/tests/suite/tool_harness.rs b/codex-rs/core/tests/suite/tool_harness.rs index 7e0ee338a..bb1da9e8b 100644 --- a/codex-rs/core/tests/suite/tool_harness.rs +++ b/codex-rs/core/tests/suite/tool_harness.rs @@ -3,7 +3,7 @@ use std::fs; use assert_matches::assert_matches; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::plan_tool::StepStatus; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/tools.rs b/codex-rs/core/tests/suite/tools.rs index 6dd844595..af8d70f22 100644 --- a/codex-rs/core/tests/suite/tools.rs +++ b/codex-rs/core/tests/suite/tools.rs @@ -7,8 +7,8 @@ use std::time::Instant; use anyhow::Context; use anyhow::Result; -use codex_core::features::Feature; use codex_core::sandboxing::SandboxPermissions; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; use core_test_support::assert_regex_match; diff --git a/codex-rs/core/tests/suite/undo.rs b/codex-rs/core/tests/suite/undo.rs index f059ece74..ef13b49d4 100644 --- a/codex-rs/core/tests/suite/undo.rs +++ b/codex-rs/core/tests/suite/undo.rs @@ -9,7 +9,7 @@ use anyhow::Context; use anyhow::Result; use anyhow::bail; use codex_core::CodexThread; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::Op; use codex_protocol::protocol::UndoCompletedEvent; diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index 848e77750..1e6073be0 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -4,7 +4,7 @@ use std::fs; use anyhow::Context; use anyhow::Result; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ExecCommandSource; diff --git a/codex-rs/core/tests/suite/unstable_features_warning.rs b/codex-rs/core/tests/suite/unstable_features_warning.rs index 94d7b5183..18be16b62 100644 --- a/codex-rs/core/tests/suite/unstable_features_warning.rs +++ b/codex-rs/core/tests/suite/unstable_features_warning.rs @@ -3,7 +3,7 @@ use codex_config::CONFIG_TOML_FILE; use codex_core::CodexAuth; use codex_core::NewThread; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::InitialHistory; use codex_protocol::protocol::WarningEvent; diff --git a/codex-rs/core/tests/suite/user_shell_cmd.rs b/codex-rs/core/tests/suite/user_shell_cmd.rs index c59d30287..eb593c6fe 100644 --- a/codex-rs/core/tests/suite/user_shell_cmd.rs +++ b/codex-rs/core/tests/suite/user_shell_cmd.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index 8f1d0a5fe..6c6ef7cdc 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -3,7 +3,7 @@ use base64::Engine; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use codex_core::CodexAuth; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::InputModality; diff --git a/codex-rs/core/tests/suite/web_search.rs b/codex-rs/core/tests/suite/web_search.rs index c90ca9123..509e5d4f5 100644 --- a/codex-rs/core/tests/suite/web_search.rs +++ b/codex-rs/core/tests/suite/web_search.rs @@ -1,6 +1,6 @@ #![allow(clippy::unwrap_used)] -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::WebSearchMode; use codex_protocol::protocol::SandboxPolicy; use core_test_support::responses; diff --git a/codex-rs/features/BUILD.bazel b/codex-rs/features/BUILD.bazel new file mode 100644 index 000000000..bcb084f32 --- /dev/null +++ b/codex-rs/features/BUILD.bazel @@ -0,0 +1,16 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "features", + crate_name = "codex_features", + compile_data = glob( + include = ["**"], + exclude = [ + "BUILD.bazel", + "Cargo.toml", + ], + allow_empty = True, + ) + [ + "//codex-rs:node-version.txt", + ], +) diff --git a/codex-rs/features/Cargo.toml b/codex-rs/features/Cargo.toml new file mode 100644 index 000000000..add5296d8 --- /dev/null +++ b/codex-rs/features/Cargo.toml @@ -0,0 +1,25 @@ +[package] +edition.workspace = true +license.workspace = true +name = "codex-features" +version.workspace = true + +[lib] +doctest = false +name = "codex_features" +path = "src/lib.rs" + +[lints] +workspace = true + +[dependencies] +codex-login = { workspace = true } +codex-otel = { workspace = true } +codex-protocol = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true, features = ["derive"] } +toml = { workspace = true } +tracing = { workspace = true, features = ["log"] } + +[dev-dependencies] +pretty_assertions = { workspace = true } diff --git a/codex-rs/core/src/features/legacy.rs b/codex-rs/features/src/legacy.rs similarity index 95% rename from codex-rs/core/src/features/legacy.rs rename to codex-rs/features/src/legacy.rs index 48e19c0df..2e3a0b37e 100644 --- a/codex-rs/core/src/features/legacy.rs +++ b/codex-rs/features/src/legacy.rs @@ -1,5 +1,5 @@ -use super::Feature; -use super::Features; +use crate::Feature; +use crate::Features; use tracing::info; #[derive(Clone, Copy)] @@ -47,7 +47,7 @@ const ALIASES: &[Alias] = &[ }, ]; -pub(crate) fn legacy_feature_keys() -> impl Iterator { +pub fn legacy_feature_keys() -> impl Iterator { ALIASES.iter().map(|alias| alias.legacy_key) } @@ -62,7 +62,7 @@ pub(crate) fn feature_for_key(key: &str) -> Option { } #[derive(Debug, Default)] -pub struct LegacyFeatureToggles { +pub(crate) struct LegacyFeatureToggles { pub include_apply_patch_tool: Option, pub experimental_use_freeform_apply_patch: Option, pub experimental_use_unified_exec_tool: Option, diff --git a/codex-rs/core/src/features.rs b/codex-rs/features/src/lib.rs similarity index 90% rename from codex-rs/core/src/features.rs rename to codex-rs/features/src/lib.rs index bcd064302..938d09885 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/features/src/lib.rs @@ -1,30 +1,24 @@ //! Centralized feature flags and metadata. //! -//! This module defines a small set of toggles that gate experimental and -//! optional behavior across the codebase. Instead of wiring individual -//! booleans through multiple types, call sites consult a single `Features` -//! container attached to `Config`. +//! This crate defines the feature registry plus the logic used to resolve an +//! effective feature set from config-like inputs. -use crate::auth::AuthManager; -use crate::auth::CodexAuth; -use crate::config::Config; -use crate::config::ConfigToml; -use crate::config::profile::ConfigProfile; -use crate::protocol::Event; -use crate::protocol::EventMsg; -use crate::protocol::WarningEvent; -use codex_config::CONFIG_TOML_FILE; +use codex_login::AuthManager; +use codex_login::CodexAuth; use codex_otel::SessionTelemetry; +use codex_protocol::protocol::Event; +use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::WarningEvent; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use std::collections::BTreeMap; use std::collections::BTreeSet; -use toml::Value as TomlValue; +use toml::Table; mod legacy; -pub(crate) use legacy::LegacyFeatureToggles; -pub(crate) use legacy::legacy_feature_keys; +use legacy::LegacyFeatureToggles; +pub use legacy::legacy_feature_keys; /// High-level lifecycle stage for a feature. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -49,7 +43,7 @@ impl Stage { pub fn experimental_menu_name(self) -> Option<&'static str> { match self { Stage::Experimental { name, .. } => Some(name), - _ => None, + Stage::UnderDevelopment | Stage::Stable | Stage::Deprecated | Stage::Removed => None, } } @@ -58,7 +52,7 @@ impl Stage { Stage::Experimental { menu_description, .. } => Some(menu_description), - _ => None, + Stage::UnderDevelopment | Stage::Stable | Stage::Deprecated | Stage::Removed => None, } } @@ -68,7 +62,7 @@ impl Stage { announcement: "", .. } => None, Stage::Experimental { announcement, .. } => Some(announcement), - _ => None, + Stage::UnderDevelopment | Stage::Stable | Stage::Deprecated | Stage::Removed => None, } } } @@ -207,7 +201,7 @@ impl Feature { FEATURES .iter() .find(|spec| spec.id == self) - .unwrap_or_else(|| unreachable!("missing FeatureSpec for {:?}", self)) + .unwrap_or_else(|| unreachable!("missing FeatureSpec for {self:?}")) } } @@ -232,6 +226,14 @@ pub struct FeatureOverrides { pub web_search_request: Option, } +#[derive(Debug, Clone, Copy, Default)] +pub struct FeatureConfigSource<'a> { + pub features: Option<&'a FeaturesToml>, + pub include_apply_patch_tool: Option, + pub experimental_use_freeform_apply_patch: Option, + pub experimental_use_unified_exec_tool: Option, +} + impl FeatureOverrides { fn apply(self, features: &mut Features) { LegacyFeatureToggles { @@ -286,7 +288,7 @@ impl Features { self.apps_enabled_for_auth(auth.as_ref()) } - pub(crate) fn apps_enabled_for_auth(&self, auth: Option<&CodexAuth>) -> bool { + pub fn apps_enabled_for_auth(&self, auth: Option<&CodexAuth>) -> bool { self.enabled(Feature::Apps) && auth.is_some_and(CodexAuth::is_chatgpt_auth) } @@ -387,34 +389,24 @@ impl Features { } } - pub fn from_config( - cfg: &ConfigToml, - config_profile: &ConfigProfile, + pub fn from_sources( + base: FeatureConfigSource<'_>, + profile: FeatureConfigSource<'_>, overrides: FeatureOverrides, ) -> Self { let mut features = Features::with_defaults(); - let base_legacy = LegacyFeatureToggles { - experimental_use_freeform_apply_patch: cfg.experimental_use_freeform_apply_patch, - experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool, - ..Default::default() - }; - base_legacy.apply(&mut features); + for source in [base, profile] { + LegacyFeatureToggles { + include_apply_patch_tool: source.include_apply_patch_tool, + experimental_use_freeform_apply_patch: source.experimental_use_freeform_apply_patch, + experimental_use_unified_exec_tool: source.experimental_use_unified_exec_tool, + } + .apply(&mut features); - if let Some(base_features) = cfg.features.as_ref() { - features.apply_map(&base_features.entries); - } - - let profile_legacy = LegacyFeatureToggles { - include_apply_patch_tool: config_profile.include_apply_patch_tool, - experimental_use_freeform_apply_patch: config_profile - .experimental_use_freeform_apply_patch, - - experimental_use_unified_exec_tool: config_profile.experimental_use_unified_exec_tool, - }; - profile_legacy.apply(&mut features); - if let Some(profile_features) = config_profile.features.as_ref() { - features.apply_map(&profile_features.entries); + if let Some(feature_entries) = source.features { + features.apply_map(&feature_entries.entries); + } } overrides.apply(&mut features); @@ -427,7 +419,7 @@ impl Features { self.enabled.iter().copied().collect() } - pub(crate) fn normalize_dependencies(&mut self) { + pub fn normalize_dependencies(&mut self) { if self.enabled(Feature::SpawnCsv) && !self.enabled(Feature::Collab) { self.enable(Feature::Collab); } @@ -483,7 +475,7 @@ fn web_search_details() -> &'static str { } /// Keys accepted in `[features]` tables. -pub(crate) fn feature_for_key(key: &str) -> Option { +pub fn feature_for_key(key: &str) -> Option { for spec in FEATURES { if spec.key == key { return Some(spec.id); @@ -492,7 +484,7 @@ pub(crate) fn feature_for_key(key: &str) -> Option { legacy::feature_for_key(key) } -pub(crate) fn canonical_feature_for_key(key: &str) -> Option { +pub fn canonical_feature_for_key(key: &str) -> Option { FEATURES .iter() .find(|spec| spec.key == key) @@ -871,22 +863,18 @@ pub const FEATURES: &[FeatureSpec] = &[ }, ]; -/// Push a warning event if any under-development features are enabled. -pub fn maybe_push_unstable_features_warning( - config: &Config, - post_session_configured_events: &mut Vec, -) { - if config.suppress_unstable_features_warning { - return; +pub fn unstable_features_warning_event( + effective_features: Option<&Table>, + suppress_unstable_features_warning: bool, + features: &Features, + config_path: &str, +) -> Option { + if suppress_unstable_features_warning { + return None; } let mut under_development_feature_keys = Vec::new(); - if let Some(table) = config - .config_layer_stack - .effective_config() - .get("features") - .and_then(TomlValue::as_table) - { + if let Some(table) = effective_features { for (key, value) in table { if value.as_bool() != Some(true) { continue; @@ -894,7 +882,7 @@ pub fn maybe_push_unstable_features_warning( let Some(spec) = FEATURES.iter().find(|spec| spec.key == key.as_str()) else { continue; }; - if !config.features.enabled(spec.id) { + if !features.enabled(spec.id) { continue; } if matches!(spec.stage, Stage::UnderDevelopment) { @@ -904,24 +892,18 @@ pub fn maybe_push_unstable_features_warning( } if under_development_feature_keys.is_empty() { - return; + return None; } let under_development_feature_keys = under_development_feature_keys.join(", "); - let config_path = config - .codex_home - .join(CONFIG_TOML_FILE) - .display() - .to_string(); let message = format!( "Under-development features enabled: {under_development_feature_keys}. Under-development features are incomplete and may behave unpredictably. To suppress this warning, set `suppress_unstable_features_warning = true` in {config_path}." ); - post_session_configured_events.push(Event { - id: "".to_owned(), + Some(Event { + id: String::new(), msg: EventMsg::Warning(WarningEvent { message }), - }); + }) } #[cfg(test)] -#[path = "features_tests.rs"] mod tests; diff --git a/codex-rs/core/src/features_tests.rs b/codex-rs/features/src/tests.rs similarity index 68% rename from codex-rs/core/src/features_tests.rs rename to codex-rs/features/src/tests.rs index b7784730e..faf02b083 100644 --- a/codex-rs/core/src/features_tests.rs +++ b/codex-rs/features/src/tests.rs @@ -1,10 +1,21 @@ -use super::*; - +use crate::Feature; +use crate::FeatureConfigSource; +use crate::FeatureOverrides; +use crate::Features; +use crate::FeaturesToml; +use crate::Stage; +use crate::feature_for_key; +use crate::unstable_features_warning_event; +use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::WarningEvent; use pretty_assertions::assert_eq; +use std::collections::BTreeMap; +use toml::Table; +use toml::Value as TomlValue; #[test] fn under_development_features_are_disabled_by_default() { - for spec in FEATURES { + for spec in crate::FEATURES { if matches!(spec.stage, Stage::UnderDevelopment) { assert_eq!( spec.default_enabled, false, @@ -17,7 +28,7 @@ fn under_development_features_are_disabled_by_default() { #[test] fn default_enabled_features_are_stable() { - for spec in FEATURES { + for spec in crate::FEATURES { if spec.default_enabled { assert!( matches!(spec.stage, Stage::Stable | Stage::Removed), @@ -177,9 +188,72 @@ fn apps_require_feature_flag_and_chatgpt_auth() { features.enable(Feature::Apps); assert!(!features.apps_enabled_for_auth(None)); - let api_key_auth = CodexAuth::from_api_key("test-api-key"); + let api_key_auth = codex_login::CodexAuth::from_api_key("test-api-key"); assert!(!features.apps_enabled_for_auth(Some(&api_key_auth))); - let chatgpt_auth = CodexAuth::create_dummy_chatgpt_auth_for_testing(); + let chatgpt_auth = codex_login::CodexAuth::create_dummy_chatgpt_auth_for_testing(); assert!(features.apps_enabled_for_auth(Some(&chatgpt_auth))); } + +#[test] +fn from_sources_applies_base_profile_and_overrides() { + let mut base_entries = BTreeMap::new(); + base_entries.insert("plugins".to_string(), true); + let base_features = FeaturesToml { + entries: base_entries, + }; + + let mut profile_entries = BTreeMap::new(); + profile_entries.insert("code_mode_only".to_string(), true); + let profile_features = FeaturesToml { + entries: profile_entries, + }; + + let features = Features::from_sources( + FeatureConfigSource { + features: Some(&base_features), + ..Default::default() + }, + FeatureConfigSource { + features: Some(&profile_features), + include_apply_patch_tool: Some(true), + ..Default::default() + }, + FeatureOverrides { + web_search_request: Some(false), + ..Default::default() + }, + ); + + assert_eq!(features.enabled(Feature::Plugins), true); + assert_eq!(features.enabled(Feature::CodeModeOnly), true); + assert_eq!(features.enabled(Feature::CodeMode), true); + assert_eq!(features.enabled(Feature::ApplyPatchFreeform), true); + assert_eq!(features.enabled(Feature::WebSearchRequest), false); +} + +#[test] +fn unstable_warning_event_only_mentions_enabled_under_development_features() { + let mut configured_features = Table::new(); + configured_features.insert("child_agents_md".to_string(), TomlValue::Boolean(true)); + configured_features.insert("personality".to_string(), TomlValue::Boolean(true)); + configured_features.insert("unknown".to_string(), TomlValue::Boolean(true)); + + let mut features = Features::with_defaults(); + features.enable(Feature::ChildAgentsMd); + + let warning = unstable_features_warning_event( + Some(&configured_features), + false, + &features, + "/tmp/config.toml", + ) + .expect("warning event"); + + let EventMsg::Warning(WarningEvent { message }) = warning.msg else { + panic!("expected warning event"); + }; + assert!(message.contains("child_agents_md")); + assert!(!message.contains("personality")); + assert!(message.contains("/tmp/config.toml")); +} diff --git a/codex-rs/mcp-server/Cargo.toml b/codex-rs/mcp-server/Cargo.toml index 2ecce383c..4c05f27c1 100644 --- a/codex-rs/mcp-server/Cargo.toml +++ b/codex-rs/mcp-server/Cargo.toml @@ -19,6 +19,7 @@ workspace = true anyhow = { workspace = true } codex-arg0 = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-protocol = { workspace = true } codex-shell-command = { workspace = true } codex-utils-cli = { workspace = true } diff --git a/codex-rs/mcp-server/src/message_processor.rs b/codex-rs/mcp-server/src/message_processor.rs index ee57b7038..e5397e4ca 100644 --- a/codex-rs/mcp-server/src/message_processor.rs +++ b/codex-rs/mcp-server/src/message_processor.rs @@ -7,6 +7,7 @@ use codex_core::config::Config; use codex_core::default_client::USER_AGENT_SUFFIX; use codex_core::default_client::get_codex_user_agent; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; +use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::Submission; @@ -65,7 +66,7 @@ impl MessageProcessor { CollaborationModesConfig { default_mode_request_user_input: config .features - .enabled(codex_core::features::Feature::DefaultModeRequestUserInput), + .enabled(Feature::DefaultModeRequestUserInput), }, )); Self { diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index d4b6f25f0..4827ef477 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -36,6 +36,7 @@ codex-chatgpt = { workspace = true } codex-client = { workspace = true } codex-cloud-requirements = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-feedback = { workspace = true } codex-file-search = { workspace = true } codex-login = { workspace = true } diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 50b663162..5ec4850d1 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -51,13 +51,13 @@ use codex_core::config::edit::ConfigEditsBuilder; use codex_core::config::types::ApprovalsReviewer; use codex_core::config::types::ModelAvailabilityNuxConfig; use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::features::Feature; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_core::models_manager::manager::RefreshStrategy; use codex_core::models_manager::model_presets::HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG; use codex_core::models_manager::model_presets::HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_features::Feature; use codex_otel::SessionTelemetry; use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index d8a71c3da..3adc86508 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -24,7 +24,7 @@ use crate::bottom_pane::TerminalTitleItem; use crate::history_cell::HistoryCell; use codex_core::config::types::ApprovalsReviewer; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::CollaborationModeMask; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ServiceTier; diff --git a/codex-rs/tui/src/app_server_tui_dispatch.rs b/codex-rs/tui/src/app_server_tui_dispatch.rs index e083bd319..63c5dc8dd 100644 --- a/codex-rs/tui/src/app_server_tui_dispatch.rs +++ b/codex-rs/tui/src/app_server_tui_dispatch.rs @@ -3,7 +3,7 @@ use std::future::Future; use crate::Cli; use codex_core::config::Config; use codex_core::config::ConfigOverrides; -use codex_core::features::Feature; +use codex_features::Feature; pub(crate) fn app_server_tui_config_inputs( cli: &Cli, diff --git a/codex-rs/tui/src/bottom_pane/approval_overlay.rs b/codex-rs/tui/src/bottom_pane/approval_overlay.rs index 72fe3e48e..1b403c251 100644 --- a/codex-rs/tui/src/bottom_pane/approval_overlay.rs +++ b/codex-rs/tui/src/bottom_pane/approval_overlay.rs @@ -16,7 +16,7 @@ use crate::key_hint::KeyBinding; use crate::render::highlight::highlight_bash_to_lines; use crate::render::renderable::ColumnRenderable; use crate::render::renderable::Renderable; -use codex_core::features::Features; +use codex_features::Features; use codex_protocol::ThreadId; use codex_protocol::mcp::RequestId; use codex_protocol::models::MacOsAutomationPermission; diff --git a/codex-rs/tui/src/bottom_pane/experimental_features_view.rs b/codex-rs/tui/src/bottom_pane/experimental_features_view.rs index 8a81f1f98..c36d70c9f 100644 --- a/codex-rs/tui/src/bottom_pane/experimental_features_view.rs +++ b/codex-rs/tui/src/bottom_pane/experimental_features_view.rs @@ -19,7 +19,7 @@ use crate::render::renderable::ColumnRenderable; use crate::render::renderable::Renderable; use crate::style::user_message_style; -use codex_core::features::Feature; +use codex_features::Feature; use super::CancellationEvent; use super::bottom_pane_view::BottomPaneView; diff --git a/codex-rs/tui/src/bottom_pane/mod.rs b/codex-rs/tui/src/bottom_pane/mod.rs index 80f35d5ff..56b25dfa1 100644 --- a/codex-rs/tui/src/bottom_pane/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mod.rs @@ -27,9 +27,9 @@ use crate::render::renderable::Renderable; use crate::render::renderable::RenderableItem; use crate::tui::FrameRequester; use bottom_pane_view::BottomPaneView; -use codex_core::features::Features; use codex_core::plugins::PluginCapabilitySummary; use codex_core::skills::model::SkillMetadata; +use codex_features::Features; use codex_file_search::FileMatch; use codex_protocol::request_user_input::RequestUserInputEvent; use codex_protocol::user_input::TextElement; diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 6183aacd8..cdb3c2f78 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -67,8 +67,6 @@ use codex_core::config::types::ApprovalsReviewer; use codex_core::config::types::Notifications; use codex_core::config::types::WindowsSandboxModeToml; use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::features::FEATURES; -use codex_core::features::Feature; use codex_core::find_thread_name_by_id; use codex_core::git_info::current_branch_name; use codex_core::git_info::get_git_repo_root; @@ -80,6 +78,8 @@ use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use codex_core::skills::model::SkillMetadata; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_features::FEATURES; +use codex_features::Feature; use codex_otel::RuntimeMetricsSummary; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index cc91dce6f..ce6d2776c 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -33,11 +33,11 @@ use codex_core::config_loader::ConfigLayerStack; use codex_core::config_loader::ConfigRequirements; use codex_core::config_loader::ConfigRequirementsToml; use codex_core::config_loader::RequirementSource; -use codex_core::features::FEATURES; -use codex_core::features::Feature; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_core::models_manager::manager::ModelsManager; use codex_core::skills::model::SkillMetadata; +use codex_features::FEATURES; +use codex_features::Feature; use codex_otel::RuntimeMetricsSummary; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index d35db703d..ae2e53902 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -1246,7 +1246,7 @@ mod tests { use codex_core::config::ConfigBuilder; use codex_core::config::ConfigOverrides; use codex_core::config::ProjectConfig; - use codex_core::features::Feature; + use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::RolloutLine; diff --git a/codex-rs/tui/src/tooltips.rs b/codex-rs/tui/src/tooltips.rs index c2719b1cb..b5064c8e7 100644 --- a/codex-rs/tui/src/tooltips.rs +++ b/codex-rs/tui/src/tooltips.rs @@ -1,4 +1,4 @@ -use codex_core::features::FEATURES; +use codex_features::FEATURES; use codex_protocol::account::PlanType; use lazy_static::lazy_static; use rand::Rng; diff --git a/codex-rs/tui_app_server/Cargo.toml b/codex-rs/tui_app_server/Cargo.toml index 4d9b26889..886604205 100644 --- a/codex-rs/tui_app_server/Cargo.toml +++ b/codex-rs/tui_app_server/Cargo.toml @@ -41,6 +41,7 @@ codex-chatgpt = { workspace = true } codex-client = { workspace = true } codex-cloud-requirements = { workspace = true } codex-core = { workspace = true } +codex-features = { workspace = true } codex-feedback = { workspace = true } codex-file-search = { workspace = true } codex-login = { workspace = true } diff --git a/codex-rs/tui_app_server/src/app.rs b/codex-rs/tui_app_server/src/app.rs index 52b6db48b..87024c12d 100644 --- a/codex-rs/tui_app_server/src/app.rs +++ b/codex-rs/tui_app_server/src/app.rs @@ -68,13 +68,13 @@ use codex_core::config::edit::ConfigEditsBuilder; use codex_core::config::types::ApprovalsReviewer; use codex_core::config::types::ModelAvailabilityNuxConfig; use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::features::Feature; use codex_core::message_history; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_core::models_manager::model_presets::HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG; use codex_core::models_manager::model_presets::HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_features::Feature; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; use codex_protocol::approvals::ExecApprovalRequestEvent; diff --git a/codex-rs/tui_app_server/src/app_event.rs b/codex-rs/tui_app_server/src/app_event.rs index c7569cf13..afbd4e44f 100644 --- a/codex-rs/tui_app_server/src/app_event.rs +++ b/codex-rs/tui_app_server/src/app_event.rs @@ -25,7 +25,7 @@ use crate::bottom_pane::StatusLineItem; use crate::history_cell::HistoryCell; use codex_core::config::types::ApprovalsReviewer; -use codex_core::features::Feature; +use codex_features::Feature; use codex_protocol::config_types::CollaborationModeMask; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ServiceTier; diff --git a/codex-rs/tui_app_server/src/bottom_pane/approval_overlay.rs b/codex-rs/tui_app_server/src/bottom_pane/approval_overlay.rs index ac9fd3d4e..f5d1cee62 100644 --- a/codex-rs/tui_app_server/src/bottom_pane/approval_overlay.rs +++ b/codex-rs/tui_app_server/src/bottom_pane/approval_overlay.rs @@ -16,7 +16,7 @@ use crate::key_hint::KeyBinding; use crate::render::highlight::highlight_bash_to_lines; use crate::render::renderable::ColumnRenderable; use crate::render::renderable::Renderable; -use codex_core::features::Features; +use codex_features::Features; use codex_protocol::ThreadId; use codex_protocol::mcp::RequestId; use codex_protocol::models::MacOsAutomationPermission; diff --git a/codex-rs/tui_app_server/src/bottom_pane/experimental_features_view.rs b/codex-rs/tui_app_server/src/bottom_pane/experimental_features_view.rs index 8a81f1f98..c36d70c9f 100644 --- a/codex-rs/tui_app_server/src/bottom_pane/experimental_features_view.rs +++ b/codex-rs/tui_app_server/src/bottom_pane/experimental_features_view.rs @@ -19,7 +19,7 @@ use crate::render::renderable::ColumnRenderable; use crate::render::renderable::Renderable; use crate::style::user_message_style; -use codex_core::features::Feature; +use codex_features::Feature; use super::CancellationEvent; use super::bottom_pane_view::BottomPaneView; diff --git a/codex-rs/tui_app_server/src/bottom_pane/mod.rs b/codex-rs/tui_app_server/src/bottom_pane/mod.rs index 11291b1a5..c7d63be40 100644 --- a/codex-rs/tui_app_server/src/bottom_pane/mod.rs +++ b/codex-rs/tui_app_server/src/bottom_pane/mod.rs @@ -27,9 +27,9 @@ use crate::render::renderable::Renderable; use crate::render::renderable::RenderableItem; use crate::tui::FrameRequester; use bottom_pane_view::BottomPaneView; -use codex_core::features::Features; use codex_core::plugins::PluginCapabilitySummary; use codex_core::skills::model::SkillMetadata; +use codex_features::Features; use codex_file_search::FileMatch; use codex_protocol::request_user_input::RequestUserInputEvent; use codex_protocol::user_input::TextElement; diff --git a/codex-rs/tui_app_server/src/chatwidget.rs b/codex-rs/tui_app_server/src/chatwidget.rs index b233527fa..5bb2dbff4 100644 --- a/codex-rs/tui_app_server/src/chatwidget.rs +++ b/codex-rs/tui_app_server/src/chatwidget.rs @@ -87,8 +87,6 @@ use codex_core::config::types::ApprovalsReviewer; use codex_core::config::types::Notifications; use codex_core::config::types::WindowsSandboxModeToml; use codex_core::config_loader::ConfigLayerStackOrdering; -use codex_core::features::FEATURES; -use codex_core::features::Feature; use codex_core::find_thread_name_by_id; use codex_core::git_info::current_branch_name; use codex_core::git_info::get_git_repo_root; @@ -98,6 +96,8 @@ use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use codex_core::skills::model::SkillMetadata; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_features::FEATURES; +use codex_features::Feature; use codex_otel::RuntimeMetricsSummary; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; diff --git a/codex-rs/tui_app_server/src/chatwidget/tests.rs b/codex-rs/tui_app_server/src/chatwidget/tests.rs index 2b14dac16..6ddf50e3f 100644 --- a/codex-rs/tui_app_server/src/chatwidget/tests.rs +++ b/codex-rs/tui_app_server/src/chatwidget/tests.rs @@ -57,10 +57,10 @@ use codex_core::config_loader::ConfigLayerStack; use codex_core::config_loader::ConfigRequirements; use codex_core::config_loader::ConfigRequirementsToml; use codex_core::config_loader::RequirementSource; -use codex_core::features::FEATURES; -use codex_core::features::Feature; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_core::skills::model::SkillMetadata; +use codex_features::FEATURES; +use codex_features::Feature; use codex_otel::RuntimeMetricsSummary; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; diff --git a/codex-rs/tui_app_server/src/lib.rs b/codex-rs/tui_app_server/src/lib.rs index c296d0d62..17e309d5f 100644 --- a/codex-rs/tui_app_server/src/lib.rs +++ b/codex-rs/tui_app_server/src/lib.rs @@ -1594,7 +1594,7 @@ mod tests { use codex_core::config::ConfigBuilder; use codex_core::config::ConfigOverrides; use codex_core::config::ProjectConfig; - use codex_core::features::Feature; + use codex_features::Feature; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::RolloutLine; diff --git a/codex-rs/tui_app_server/src/tooltips.rs b/codex-rs/tui_app_server/src/tooltips.rs index c2719b1cb..b5064c8e7 100644 --- a/codex-rs/tui_app_server/src/tooltips.rs +++ b/codex-rs/tui_app_server/src/tooltips.rs @@ -1,4 +1,4 @@ -use codex_core::features::FEATURES; +use codex_features::FEATURES; use codex_protocol::account::PlanType; use lazy_static::lazy_static; use rand::Rng;