mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Deprecate the multi_agent feature flag (#59479)
## What changed Mark `[features].multi_agent` as deprecated while keeping it enabled by default and preserving its control of the V1 fallback. Emit a startup warning when a user or project configuration explicitly sets the flag. The warning points to `[agents].enabled = false` for disabling subagent tools, with an exception when `[features].multi_agent_v2` is enabled. Session-only overrides do not trigger the warning. ## Testing Add configuration-loader coverage for explicit user and project settings, including a project setting overridden by a session flag, and for session-only overrides. Update the default-enabled feature check to allow deprecated features. GitOrigin-RevId: 8bcac6ce5eac27528a2b362755e25d6a6e811c59
This commit is contained in:
@@ -506,6 +506,21 @@ pub async fn load_config_layers_state(
|
||||
ignore_user_and_project_exec_policy_rules,
|
||||
);
|
||||
config_layer_stack.is_projectless = is_projectless;
|
||||
if config_layer_stack.layers_low_to_high().any(|layer| {
|
||||
matches!(
|
||||
layer.name,
|
||||
ConfigLayerSource::User { .. } | ConfigLayerSource::Project { .. }
|
||||
) && layer
|
||||
.config
|
||||
.get("features")
|
||||
.and_then(|features| features.get("multi_agent"))
|
||||
.and_then(TomlValue::as_bool)
|
||||
.is_some()
|
||||
}) {
|
||||
startup_warnings.push(
|
||||
"`[features].multi_agent` is deprecated. It still controls the V1 fallback. `[agents].enabled = false` disables subagent tools even when the model selects a subagent version, unless `[features].multi_agent_v2` is enabled.".to_string(),
|
||||
);
|
||||
}
|
||||
startup_warnings.extend(ignored_config_warning(
|
||||
&config_layer_stack,
|
||||
&requirements_layers,
|
||||
|
||||
@@ -718,6 +718,63 @@ async fn missing_packaged_defaults_file_returns_an_error() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_warns_for_explicit_user_and_project_settings() -> std::io::Result<()> {
|
||||
let home = tempdir()?;
|
||||
let workspace = tempdir()?;
|
||||
let cwd = AbsolutePathBuf::try_from(workspace.path())?;
|
||||
let trust_key = TomlValue::String(project_trust_key(workspace.path()));
|
||||
let project_config_dir = workspace.path().join(".codex");
|
||||
std::fs::create_dir(&project_config_dir)?;
|
||||
let session_flags = [(
|
||||
"features.multi_agent".to_string(),
|
||||
TomlValue::Boolean(false),
|
||||
)];
|
||||
for (user, project, flags, warn) in [
|
||||
("", "", session_flags.as_slice(), false),
|
||||
("[features]\nmulti_agent = false", "", &[], true),
|
||||
(
|
||||
"",
|
||||
"[features]\nmulti_agent = true",
|
||||
session_flags.as_slice(),
|
||||
true,
|
||||
),
|
||||
] {
|
||||
std::fs::write(
|
||||
home.path().join(CONFIG_TOML_FILE),
|
||||
format!("[projects.{trust_key}]\ntrust_level = 'trusted'\n{user}"),
|
||||
)?;
|
||||
std::fs::write(project_config_dir.join(CONFIG_TOML_FILE), project)?;
|
||||
let stack = load_config_layers_state(
|
||||
&TestFileSystem,
|
||||
home.path(),
|
||||
Some(cwd.clone()),
|
||||
flags,
|
||||
LoaderOverrides::without_managed_config_for_tests(),
|
||||
&crate::NoopThreadConfigLoader,
|
||||
)
|
||||
.await?;
|
||||
let warned = stack
|
||||
.startup_warnings()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.any(|warning| {
|
||||
warning.contains(
|
||||
"`[features].multi_agent` is deprecated. It still controls the V1 fallback.",
|
||||
)
|
||||
});
|
||||
assert_eq!(
|
||||
(
|
||||
stack.effective_config()["features"]["multi_agent"].as_bool(),
|
||||
warned
|
||||
),
|
||||
(Some(false), warn),
|
||||
"user={user:?}, project={project:?}"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[tokio::test]
|
||||
async fn default_windows_managed_config_is_ignored_with_warning() {
|
||||
|
||||
@@ -1630,7 +1630,7 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::Collab,
|
||||
key: "multi_agent",
|
||||
stage: Stage::Stable,
|
||||
stage: Stage::Deprecated,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
|
||||
@@ -142,13 +142,16 @@ fn codex_apps_mcp_protocol_can_be_enabled_independently_of_generic_mcp() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_enabled_features_are_stable() {
|
||||
fn default_enabled_features_have_shipped() {
|
||||
for spec in crate::FEATURES {
|
||||
// Instant interrupts are enabled by default while retaining their development stage.
|
||||
if spec.default_enabled && spec.id != Feature::InstantInterrupt {
|
||||
assert!(
|
||||
matches!(spec.stage, Stage::Stable | Stage::Removed),
|
||||
"feature `{}` is enabled by default but is not stable/removed ({:?})",
|
||||
matches!(
|
||||
spec.stage,
|
||||
Stage::Stable | Stage::Deprecated | Stage::Removed
|
||||
),
|
||||
"feature `{}` is enabled by default at stage {:?}",
|
||||
spec.key,
|
||||
spec.stage
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user