mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Allow disabling the multi-agent wait tool (#34887)
## What changed - Add a default-on `features.multi_agent_v2.wait_agent_enabled` setting. - Omit `collaboration.wait_agent` from the tool plan when the setting is disabled, independently of `features.current_time_reminder.sleep_tool`. ## Testing - Cover configuration parsing and tool exposure with both clock sleep states. GitOrigin-RevId: c2f34c5edd5bc191d6a5ac41d7bbb80708da1202
This commit is contained in:
1 parent
6e0455fdc4
commit
4462b9deef
8 files changed
+149
-7
No files matched your search
@@ -1824,6 +1824,10 @@
|
|||||||
},
|
},
|
||||||
"usage_hint_text": {
|
"usage_hint_text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"wait_agent_enabled": {
|
||||||
|
"description": "Expose the multi-agent v2 `wait_agent` tool.",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
|||||||
@@ -10602,6 +10602,7 @@ multi_agent_mode_hint_text = "Custom mode guidance."
|
|||||||
tool_namespace = "agents"
|
tool_namespace = "agents"
|
||||||
hide_spawn_agent_metadata = true
|
hide_spawn_agent_metadata = true
|
||||||
expose_spawn_agent_model_overrides = false
|
expose_spawn_agent_model_overrides = false
|
||||||
|
wait_agent_enabled = false
|
||||||
non_code_mode_only = true
|
non_code_mode_only = true
|
||||||
|
|
||||||
[agents]
|
[agents]
|
||||||
@@ -10649,6 +10650,7 @@ max_concurrent_threads_per_session = 9
|
|||||||
);
|
);
|
||||||
assert!(config.multi_agent_v2.hide_spawn_agent_metadata);
|
assert!(config.multi_agent_v2.hide_spawn_agent_metadata);
|
||||||
assert!(!config.multi_agent_v2.expose_spawn_agent_model_overrides);
|
assert!(!config.multi_agent_v2.expose_spawn_agent_model_overrides);
|
||||||
|
assert!(!config.multi_agent_v2.wait_agent_enabled);
|
||||||
assert!(config.multi_agent_v2.non_code_mode_only);
|
assert!(config.multi_agent_v2.non_code_mode_only);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -10752,6 +10754,48 @@ fn multi_agent_v2_exposes_model_overrides_by_default() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn multi_agent_v2_allows_disabled_wait_agent_without_sleep_tool() -> std::io::Result<()> {
|
||||||
|
for config_toml in [
|
||||||
|
r#"
|
||||||
|
[features.multi_agent_v2]
|
||||||
|
enabled = true
|
||||||
|
wait_agent_enabled = false
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
[features.multi_agent_v2]
|
||||||
|
enabled = true
|
||||||
|
wait_agent_enabled = false
|
||||||
|
|
||||||
|
[features.current_time_reminder]
|
||||||
|
enabled = true
|
||||||
|
sleep_tool = false
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
[features.multi_agent_v2]
|
||||||
|
enabled = true
|
||||||
|
wait_agent_enabled = false
|
||||||
|
|
||||||
|
[features.current_time_reminder]
|
||||||
|
enabled = false
|
||||||
|
sleep_tool = true
|
||||||
|
"#,
|
||||||
|
] {
|
||||||
|
let codex_home = tempdir()?;
|
||||||
|
let config_toml = toml::from_str(config_toml).expect("TOML should deserialize");
|
||||||
|
let config = Config::load_from_base_config_with_overrides(
|
||||||
|
config_toml,
|
||||||
|
ConfigOverrides::default(),
|
||||||
|
codex_home.abs(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert!(!config.multi_agent_v2.wait_agent_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_agent_v2_preserves_empty_mode_hint_override() {
|
fn multi_agent_v2_preserves_empty_mode_hint_override() {
|
||||||
let config_toml = toml::from_str(
|
let config_toml = toml::from_str(
|
||||||
|
|||||||
@@ -1177,6 +1177,7 @@ pub struct MultiAgentV2Config {
|
|||||||
pub tool_namespace: Option<String>,
|
pub tool_namespace: Option<String>,
|
||||||
pub hide_spawn_agent_metadata: bool,
|
pub hide_spawn_agent_metadata: bool,
|
||||||
pub expose_spawn_agent_model_overrides: bool,
|
pub expose_spawn_agent_model_overrides: bool,
|
||||||
|
pub wait_agent_enabled: bool,
|
||||||
pub non_code_mode_only: bool,
|
pub non_code_mode_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1200,6 +1201,7 @@ impl MultiAgentV2Config {
|
|||||||
tool_namespace: Some(DEFAULT_MULTI_AGENT_V2_TOOL_NAMESPACE.to_string()),
|
tool_namespace: Some(DEFAULT_MULTI_AGENT_V2_TOOL_NAMESPACE.to_string()),
|
||||||
hide_spawn_agent_metadata: true,
|
hide_spawn_agent_metadata: true,
|
||||||
expose_spawn_agent_model_overrides: true,
|
expose_spawn_agent_model_overrides: true,
|
||||||
|
wait_agent_enabled: true,
|
||||||
non_code_mode_only: true,
|
non_code_mode_only: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2568,6 +2570,9 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
|
|||||||
let expose_spawn_agent_model_overrides = base
|
let expose_spawn_agent_model_overrides = base
|
||||||
.and_then(|config| config.expose_spawn_agent_model_overrides)
|
.and_then(|config| config.expose_spawn_agent_model_overrides)
|
||||||
.unwrap_or(default.expose_spawn_agent_model_overrides);
|
.unwrap_or(default.expose_spawn_agent_model_overrides);
|
||||||
|
let wait_agent_enabled = base
|
||||||
|
.and_then(|config| config.wait_agent_enabled)
|
||||||
|
.unwrap_or(default.wait_agent_enabled);
|
||||||
let mut default_root_agent_usage_hint_text = default.root_agent_usage_hint_text;
|
let mut default_root_agent_usage_hint_text = default.root_agent_usage_hint_text;
|
||||||
let mut default_subagent_usage_hint_text = default.subagent_usage_hint_text;
|
let mut default_subagent_usage_hint_text = default.subagent_usage_hint_text;
|
||||||
if expose_spawn_agent_model_overrides {
|
if expose_spawn_agent_model_overrides {
|
||||||
@@ -2612,6 +2617,7 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
|
|||||||
tool_namespace,
|
tool_namespace,
|
||||||
hide_spawn_agent_metadata,
|
hide_spawn_agent_metadata,
|
||||||
expose_spawn_agent_model_overrides,
|
expose_spawn_agent_model_overrides,
|
||||||
|
wait_agent_enabled,
|
||||||
non_code_mode_only,
|
non_code_mode_only,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -835,13 +835,15 @@ fn add_collaboration_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mu
|
|||||||
multi_agent_v2_handler(FollowupTaskHandlerV2, tool_namespace),
|
multi_agent_v2_handler(FollowupTaskHandlerV2, tool_namespace),
|
||||||
exposure,
|
exposure,
|
||||||
));
|
));
|
||||||
planned_tools.add_arc(override_tool_exposure(
|
if turn_context.config.multi_agent_v2.wait_agent_enabled {
|
||||||
multi_agent_v2_handler(
|
planned_tools.add_arc(override_tool_exposure(
|
||||||
WaitAgentHandlerV2::new(context.wait_agent_timeouts),
|
multi_agent_v2_handler(
|
||||||
tool_namespace,
|
WaitAgentHandlerV2::new(context.wait_agent_timeouts),
|
||||||
),
|
tool_namespace,
|
||||||
exposure,
|
),
|
||||||
));
|
exposure,
|
||||||
|
));
|
||||||
|
}
|
||||||
planned_tools.add_arc(override_tool_exposure(
|
planned_tools.add_arc(override_tool_exposure(
|
||||||
multi_agent_v2_handler(InterruptAgentHandler, tool_namespace),
|
multi_agent_v2_handler(InterruptAgentHandler, tool_namespace),
|
||||||
exposure,
|
exposure,
|
||||||
|
|||||||
@@ -1340,6 +1340,30 @@ async fn multi_agent_v2_message_schemas_are_encrypted() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn multi_agent_v2_can_disable_wait_agent() {
|
||||||
|
let plan = probe(|turn| {
|
||||||
|
set_feature(turn, Feature::MultiAgentV2, /*enabled*/ true);
|
||||||
|
update_config(turn, |config| {
|
||||||
|
config.multi_agent_v2.wait_agent_enabled = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
plan.namespace_function_names(MULTI_AGENT_V2_NAMESPACE),
|
||||||
|
&[
|
||||||
|
"followup_task".to_string(),
|
||||||
|
"interrupt_agent".to_string(),
|
||||||
|
"list_agents".to_string(),
|
||||||
|
"send_message".to_string(),
|
||||||
|
"spawn_agent".to_string(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
plan.assert_visible_lacks(&["clock"]);
|
||||||
|
plan.assert_registered_lacks(&["collaboration.wait_agent", "clock.sleep"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn tool_mode_selector_overrides_feature_flags() {
|
async fn tool_mode_selector_overrides_feature_flags() {
|
||||||
let direct = probe(|turn| {
|
let direct = probe(|turn| {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use test_case::test_case;
|
|||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
const MULTI_AGENT_V1_NAMESPACE: &str = "multi_agent_v1";
|
const MULTI_AGENT_V1_NAMESPACE: &str = "multi_agent_v1";
|
||||||
|
const MULTI_AGENT_V2_NAMESPACE: &str = "collaboration";
|
||||||
const SPAWN_AGENT_TOOL_NAME: &str = "spawn_agent";
|
const SPAWN_AGENT_TOOL_NAME: &str = "spawn_agent";
|
||||||
|
|
||||||
fn spawn_agent_description(body: &Value) -> Option<String> {
|
fn spawn_agent_description(body: &Value) -> Option<String> {
|
||||||
@@ -304,3 +305,59 @@ async fn configured_agent_roles_control_spawn_agent_type(
|
|||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(true, false; "wait agent remains available without clock sleep")]
|
||||||
|
#[test_case(true, true; "wait agent remains available with clock sleep")]
|
||||||
|
#[test_case(false, false; "wait agent can be disabled without clock sleep")]
|
||||||
|
#[test_case(false, true; "wait agent can be disabled with clock sleep")]
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn multi_agent_v2_wait_agent_tool_follows_configuration(
|
||||||
|
wait_agent_enabled: bool,
|
||||||
|
sleep_tool_enabled: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let current_time_reminder = if sleep_tool_enabled {
|
||||||
|
r#"
|
||||||
|
[features.current_time_reminder]
|
||||||
|
enabled = true
|
||||||
|
sleep_tool = true
|
||||||
|
"#
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let config_toml = format!(
|
||||||
|
r#"
|
||||||
|
[features.multi_agent_v2]
|
||||||
|
enabled = true
|
||||||
|
wait_agent_enabled = {wait_agent_enabled}
|
||||||
|
{current_time_reminder}"#
|
||||||
|
);
|
||||||
|
let server = start_mock_server().await;
|
||||||
|
let response = mount_sse_once(
|
||||||
|
&server,
|
||||||
|
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let test = test_codex()
|
||||||
|
.with_pre_build_hook(move |home| {
|
||||||
|
std::fs::write(home.join("config.toml"), &config_toml)
|
||||||
|
.expect("write multi-agent configuration");
|
||||||
|
})
|
||||||
|
.build_with_auto_env(&server)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
test.submit_turn("hello").await?;
|
||||||
|
|
||||||
|
let request = response.single_request();
|
||||||
|
let body = request.body_json();
|
||||||
|
assert!(namespace_child_tool(&body, MULTI_AGENT_V2_NAMESPACE, SPAWN_AGENT_TOOL_NAME).is_some());
|
||||||
|
assert_eq!(
|
||||||
|
namespace_child_tool(&body, MULTI_AGENT_V2_NAMESPACE, "wait_agent").is_some(),
|
||||||
|
wait_agent_enabled
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
namespace_child_tool(&body, "clock", "sleep").is_some(),
|
||||||
|
sleep_tool_enabled
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -66,6 +66,9 @@ pub struct MultiAgentV2ConfigToml {
|
|||||||
/// corresponding guidance to root and subagent usage hints.
|
/// corresponding guidance to root and subagent usage hints.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub expose_spawn_agent_model_overrides: Option<bool>,
|
pub expose_spawn_agent_model_overrides: Option<bool>,
|
||||||
|
/// Expose the multi-agent v2 `wait_agent` tool.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub wait_agent_enabled: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub non_code_mode_only: Option<bool>,
|
pub non_code_mode_only: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ multi_agent_mode_hint_text = "Custom mode guidance."
|
|||||||
tool_namespace = "agents"
|
tool_namespace = "agents"
|
||||||
hide_spawn_agent_metadata = true
|
hide_spawn_agent_metadata = true
|
||||||
expose_spawn_agent_model_overrides = true
|
expose_spawn_agent_model_overrides = true
|
||||||
|
wait_agent_enabled = false
|
||||||
non_code_mode_only = true
|
non_code_mode_only = true
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@@ -437,6 +438,7 @@ non_code_mode_only = true
|
|||||||
tool_namespace: Some("agents".to_string()),
|
tool_namespace: Some("agents".to_string()),
|
||||||
hide_spawn_agent_metadata: Some(true),
|
hide_spawn_agent_metadata: Some(true),
|
||||||
expose_spawn_agent_model_overrides: Some(true),
|
expose_spawn_agent_model_overrides: Some(true),
|
||||||
|
wait_agent_enabled: Some(false),
|
||||||
non_code_mode_only: Some(true),
|
non_code_mode_only: Some(true),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user