Gate the file-copy extension behind multi_env_copy_file (#54741)

## What changed

Add the `multi_env_copy_file` feature flag, disabled by default and marked as under development, to the feature registry and configuration schema. Install the file-copy extension in app-server threads and CLI debug prompt construction when the flag is enabled.

GitOrigin-RevId: 6d8b4c7479974f4b70c39f52333491c13d60d2a2
This commit is contained in:
Adam Perry @ OpenAI
2026-10-17 00:02:06 +00:00
committed by copyberry
parent 527c5deb74
commit 82d958597f
10 changed files with 67 additions and 28 deletions
+2
View File
@@ -2121,6 +2121,7 @@ dependencies = [
"codex-model-provider",
"codex-model-provider-info",
"codex-models-manager",
"codex-multi-env-copy-file",
"codex-network-proxy",
"codex-otel",
"codex-plugin",
@@ -2547,6 +2548,7 @@ dependencies = [
"codex-memories-write",
"codex-model-provider",
"codex-models-manager",
"codex-multi-env-copy-file",
"codex-plugin",
"codex-protocol",
"codex-responses-api-proxy",
+1
View File
@@ -225,6 +225,7 @@ codex-exec-server-test-support = { path = "exec-server/tests/support" }
codex-execpolicy = { path = "execpolicy" }
codex-extension-api = { path = "ext/extension-api" }
codex-extension-items = { path = "ext/items" }
codex-multi-env-copy-file = { path = "ext/multi-env-copy-file" }
codex-goal-extension = { path = "ext/goal" }
codex-git-attribution = { path = "ext/git-attribution" }
codex-guardian-reviewer = { path = "ext/guardian-reviewer" }
+1
View File
@@ -51,6 +51,7 @@ codex-exec-server = { workspace = true }
codex-extension-api = { workspace = true }
codex-external-agent-migration = { workspace = true }
codex-features = { workspace = true }
codex-multi-env-copy-file = { workspace = true }
codex-goal-extension = { workspace = true }
codex-git-attribution = { workspace = true }
codex-guardian-v2 = { workspace = true }
+7
View File
@@ -48,6 +48,7 @@ pub(crate) struct ThreadExtensionDependencies {
}
pub(crate) fn thread_extensions(
config: &Config,
dependencies: ThreadExtensionDependencies,
) -> Arc<ExtensionRegistry<Config>> {
let ThreadExtensionDependencies {
@@ -65,6 +66,12 @@ pub(crate) fn thread_extensions(
turn_start_admission,
} = dependencies;
let mut builder = ExtensionRegistryBuilder::<Config>::with_event_sink(Arc::clone(&event_sink));
if config
.features
.enabled(codex_features::Feature::MultiEnvCopyFile)
{
codex_multi_env_copy_file::install(&mut builder);
}
if let Some(admission) = turn_start_admission {
builder.turn_start_admission(admission);
}
+17 -14
View File
@@ -476,20 +476,23 @@ enabled = false
codex_core::CodexAppsToolsCache::default(),
SessionSource::Exec,
Arc::clone(&environment_manager),
thread_extensions(ThreadExtensionDependencies {
event_sink: Arc::new(NoopExtensionEventSink),
auth_manager: auth_manager.clone(),
state_db: Some(state_db.clone()),
analytics_events_client: codex_analytics::AnalyticsEventsClient::disabled(),
thread_manager: thread_manager.clone(),
goal_service: Arc::new(codex_goal_extension::GoalService::new()),
environment_manager: Arc::clone(&environment_manager),
executor_skill_provider: Arc::clone(&executor_skill_provider),
git_attribution_base_url: good_config.chatgpt_base_url.clone(),
http_client_factory: good_config.http_client_factory(),
queue_service: None,
turn_start_admission: None,
}),
thread_extensions(
&good_config,
ThreadExtensionDependencies {
event_sink: Arc::new(NoopExtensionEventSink),
auth_manager: auth_manager.clone(),
state_db: Some(state_db.clone()),
analytics_events_client: codex_analytics::AnalyticsEventsClient::disabled(),
thread_manager: thread_manager.clone(),
goal_service: Arc::new(codex_goal_extension::GoalService::new()),
environment_manager: Arc::clone(&environment_manager),
executor_skill_provider: Arc::clone(&executor_skill_provider),
git_attribution_base_url: good_config.chatgpt_base_url.clone(),
http_client_factory: good_config.http_client_factory(),
queue_service: None,
turn_start_admission: None,
},
),
Arc::new(CodexHomeUserInstructionsProvider::new(
good_config.codex_home.clone(),
)),
+17 -14
View File
@@ -344,20 +344,23 @@ impl MessageProcessor {
codex_core::CodexAppsToolsCache::default(),
session_source,
environment_manager,
thread_extensions(ThreadExtensionDependencies {
event_sink: Arc::clone(&extension_event_sink),
auth_manager: auth_manager.clone(),
state_db: state_db.clone(),
analytics_events_client: analytics_events_client.clone(),
thread_manager: thread_manager.clone(),
goal_service: Arc::clone(&goal_service),
environment_manager: Arc::clone(&environment_manager_for_extensions),
executor_skill_provider: Arc::clone(&executor_skill_provider),
git_attribution_base_url: config.chatgpt_base_url.clone(),
http_client_factory: config.http_client_factory(),
queue_service: queue_service.clone(),
turn_start_admission: Some(Arc::clone(&turn_start_admission)),
}),
thread_extensions(
config.as_ref(),
ThreadExtensionDependencies {
event_sink: Arc::clone(&extension_event_sink),
auth_manager: auth_manager.clone(),
state_db: state_db.clone(),
analytics_events_client: analytics_events_client.clone(),
thread_manager: thread_manager.clone(),
goal_service: Arc::clone(&goal_service),
environment_manager: Arc::clone(&environment_manager_for_extensions),
executor_skill_provider: Arc::clone(&executor_skill_provider),
git_attribution_base_url: config.chatgpt_base_url.clone(),
http_client_factory: config.http_client_factory(),
queue_service: queue_service.clone(),
turn_start_admission: Some(Arc::clone(&turn_start_admission)),
},
),
Arc::new(CodexHomeUserInstructionsProvider::new(
config.codex_home.clone(),
)),
+1
View File
@@ -55,6 +55,7 @@ codex-exec-server = { workspace = true }
codex-execpolicy = { workspace = true }
codex-extension-api = { workspace = true }
codex-features = { workspace = true }
codex-multi-env-copy-file = { workspace = true }
codex-git-attribution = { workspace = true }
codex-git-utils = { workspace = true }
codex-install-context = { workspace = true }
+7
View File
@@ -2099,6 +2099,13 @@ async fn run_debug_prompt_input_command(
let auth_manager =
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await?;
let mut extensions = codex_extension_api::ExtensionRegistryBuilder::new();
// Debug prompt construction bypasses app-server and needs its own extension registry.
if config
.features
.enabled(codex_features::Feature::MultiEnvCopyFile)
{
codex_multi_env_copy_file::install(&mut extensions);
}
codex_git_attribution::install(
&mut extensions,
auth_manager,
+6
View File
@@ -1066,6 +1066,9 @@
"multi_agent_v2_dynamic_tools": {
"type": "boolean"
},
"multi_env_copy_file": {
"type": "boolean"
},
"network_proxy": {
"$ref": "#/definitions/FeatureToml_for_NetworkProxyConfigToml"
},
@@ -7452,6 +7455,9 @@
"multi_agent_v2_dynamic_tools": {
"type": "boolean"
},
"multi_env_copy_file": {
"type": "boolean"
},
"network_proxy": {
"$ref": "#/definitions/FeatureToml_for_NetworkProxyConfigToml"
},
+8
View File
@@ -122,6 +122,8 @@ pub enum Feature {
/// Advertise enabled environment-backed tools and their shell parameters before the
/// executor is ready. Actual execution still requires a usable environment and its policy.
StableEnvironmentTools,
/// Copy files between explicitly selected execution environments.
MultiEnvCopyFile,
/// Send per-content-entry classifications in internal Responses metadata.
ContentItemKinds,
/// Record model-attempted tool calls in internal Responses metadata.
@@ -1060,6 +1062,12 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::Stable,
default_enabled: true,
},
FeatureSpec {
id: Feature::MultiEnvCopyFile,
key: "multi_env_copy_file",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::SleepTool,
key: "sleep_tool",