mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Make MultiAgentV2 wait minimum configurable (#20052)
## Why MultiAgentV2 `wait_agent` currently clamps short waits to a fixed 10 second minimum. That default is still useful for preventing tight polling loops, but it is too rigid for environments that need faster mailbox wake-up checks or a larger minimum to discourage frequent polling. This PR makes the minimum wait timeout configurable from the existing MultiAgentV2 feature config section, so operators can tune the behavior without changing the legacy multi-agent tool surface. ## What Changed - Added `features.multi_agent_v2.min_wait_timeout_ms`. - Defaulted the new setting to the existing 10 second floor. - Validated the configured value as `1..=3600000`, matching the existing one hour maximum wait bound. - Applied the configured minimum to MultiAgentV2 `wait_agent` runtime clamping. - Plumbed the configured minimum into the `wait_agent` tool schema, including the effective default when the minimum is above the normal 30 second default. - Regenerated `core/config.schema.json`. ## Verification - `cargo test -p codex-features` - `cargo test -p codex-tools` - `cargo test -p codex-core --lib multi_agent_v2` - `just fix -p codex-core`
This commit is contained in:
committed by
GitHub
Unverified
parent
1de7a9bf69
commit
34d71d43eb
@@ -7804,6 +7804,7 @@ async fn multi_agent_v2_config_from_feature_table() -> std::io::Result<()> {
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
max_concurrent_threads_per_session = 5
|
||||
min_wait_timeout_ms = 2500
|
||||
usage_hint_enabled = false
|
||||
usage_hint_text = "Custom delegation guidance."
|
||||
root_agent_usage_hint_text = "Root guidance."
|
||||
@@ -7820,6 +7821,7 @@ hide_spawn_agent_metadata = true
|
||||
|
||||
assert!(config.features.enabled(Feature::MultiAgentV2));
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 5);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 2500);
|
||||
assert_eq!(config.agent_max_threads, Some(4));
|
||||
assert!(!config.multi_agent_v2.usage_hint_enabled);
|
||||
assert_eq!(
|
||||
@@ -7848,6 +7850,7 @@ async fn profile_multi_agent_v2_config_overrides_base() -> std::io::Result<()> {
|
||||
|
||||
[features.multi_agent_v2]
|
||||
max_concurrent_threads_per_session = 4
|
||||
min_wait_timeout_ms = 3000
|
||||
usage_hint_enabled = true
|
||||
usage_hint_text = "base hint"
|
||||
root_agent_usage_hint_text = "base root hint"
|
||||
@@ -7856,6 +7859,7 @@ hide_spawn_agent_metadata = true
|
||||
|
||||
[profiles.no_hint.features.multi_agent_v2]
|
||||
max_concurrent_threads_per_session = 6
|
||||
min_wait_timeout_ms = 1500
|
||||
usage_hint_enabled = false
|
||||
usage_hint_text = "profile hint"
|
||||
root_agent_usage_hint_text = "profile root hint"
|
||||
@@ -7871,6 +7875,7 @@ hide_spawn_agent_metadata = false
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 6);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 1500);
|
||||
assert!(!config.multi_agent_v2.usage_hint_enabled);
|
||||
assert_eq!(
|
||||
config.multi_agent_v2.usage_hint_text.as_deref(),
|
||||
@@ -7906,6 +7911,7 @@ enabled = true
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.multi_agent_v2.max_concurrent_threads_per_session, 4);
|
||||
assert_eq!(config.multi_agent_v2.min_wait_timeout_ms, 10_000);
|
||||
assert_eq!(config.agent_max_threads, Some(3));
|
||||
|
||||
Ok(())
|
||||
@@ -7940,6 +7946,54 @@ max_threads = 3
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_rejects_invalid_min_wait_timeout() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 0
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("zero min_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at least 1"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
min_wait_timeout_ms = 3600001
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let err = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect_err("too large min_wait_timeout_ms should be rejected");
|
||||
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most 3600000"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_session_thread_cap_one_disallows_subagents() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -156,6 +156,8 @@ impl Default for GhostSnapshotConfig {
|
||||
pub(crate) const AGENTS_MD_MAX_BYTES: usize = 32 * 1024; // 32 KiB
|
||||
pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option<usize> = Some(6);
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION: usize = 4;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
|
||||
pub(crate) const MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
|
||||
pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1;
|
||||
pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option<u64> = None;
|
||||
const LOCAL_DEV_BUILD_VERSION: &str = "0.0.0";
|
||||
@@ -753,6 +755,7 @@ pub struct Config {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MultiAgentV2Config {
|
||||
pub max_concurrent_threads_per_session: usize,
|
||||
pub min_wait_timeout_ms: i64,
|
||||
pub usage_hint_enabled: bool,
|
||||
pub usage_hint_text: Option<String>,
|
||||
pub root_agent_usage_hint_text: Option<String>,
|
||||
@@ -765,6 +768,7 @@ impl Default for MultiAgentV2Config {
|
||||
Self {
|
||||
max_concurrent_threads_per_session:
|
||||
DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION,
|
||||
min_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS,
|
||||
usage_hint_enabled: true,
|
||||
usage_hint_text: None,
|
||||
root_agent_usage_hint_text: None,
|
||||
@@ -1638,6 +1642,10 @@ fn resolve_multi_agent_v2_config(
|
||||
.and_then(|config| config.max_concurrent_threads_per_session)
|
||||
.or_else(|| base.and_then(|config| config.max_concurrent_threads_per_session))
|
||||
.unwrap_or(default.max_concurrent_threads_per_session);
|
||||
let min_wait_timeout_ms = profile
|
||||
.and_then(|config| config.min_wait_timeout_ms)
|
||||
.or_else(|| base.and_then(|config| config.min_wait_timeout_ms))
|
||||
.unwrap_or(default.min_wait_timeout_ms);
|
||||
let usage_hint_enabled = profile
|
||||
.and_then(|config| config.usage_hint_enabled)
|
||||
.or_else(|| base.and_then(|config| config.usage_hint_enabled))
|
||||
@@ -1664,6 +1672,7 @@ fn resolve_multi_agent_v2_config(
|
||||
|
||||
MultiAgentV2Config {
|
||||
max_concurrent_threads_per_session,
|
||||
min_wait_timeout_ms,
|
||||
usage_hint_enabled,
|
||||
usage_hint_text,
|
||||
root_agent_usage_hint_text,
|
||||
@@ -2186,6 +2195,20 @@ impl Config {
|
||||
"features.multi_agent_v2.max_concurrent_threads_per_session must be at least 1",
|
||||
));
|
||||
}
|
||||
if multi_agent_v2.min_wait_timeout_ms <= 0 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at least 1",
|
||||
));
|
||||
}
|
||||
if multi_agent_v2.min_wait_timeout_ms > MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"features.multi_agent_v2.min_wait_timeout_ms must be at most {MAX_MULTI_AGENT_V2_WAIT_TIMEOUT_MS}"
|
||||
),
|
||||
));
|
||||
}
|
||||
let agent_max_threads_from_config = cfg.agents.as_ref().and_then(|agents| agents.max_threads);
|
||||
let agent_max_threads = if features.enabled(Feature::MultiAgentV2) {
|
||||
if agent_max_threads_from_config.is_some() {
|
||||
|
||||
Reference in New Issue
Block a user