Add MultiAgentV2 root and subagent context hints (#19805)

## Why

MultiAgentV2 sessions need startup guidance that matches the role of the
thread that is actually being created. Root agents and subagents have
different responsibilities, and forked subagents can inherit parent
rollout history. If the parent hint is carried into the child context,
the child can see stale or conflicting developer guidance before its own
session-specific context is added.

## What changed

- Added `features.multi_agent_v2.root_agent_usage_hint_text` and
`features.multi_agent_v2.subagent_usage_hint_text` config fields,
including schema/config parsing support.
- Injected the matching root or subagent hint into the initial context
as its own developer message when `multi_agent_v2` is enabled.
- Filtered configured MultiAgentV2 usage-hint developer messages out of
forked parent history so a child thread receives fresh guidance for its
own session source/config.
- Added targeted coverage for config parsing, initial-context rendering,
feature-config deserialization, and forked-history filtering.

## Context examples

With this config:

```toml
[features.multi_agent_v2]
enabled = true
root_agent_usage_hint_text = "Root guidance."
subagent_usage_hint_text = "Subagent guidance."
```

A root thread initial context renders the root hint as a standalone
developer message:

```text
[developer]
<existing developer context, when present>

[developer]
Root guidance.
```

A subagent thread initial context renders the subagent hint instead:

```text
[developer]
<existing developer context, when present>

[developer]
Subagent guidance.
```

When a subagent forks parent history, any parent developer message whose
text exactly matches the configured MultiAgentV2 root or subagent hint
is omitted from the forked history before the child receives its fresh
subagent hint.
This commit is contained in:
jif-oai authored and GitHub committed 2026-04-28 12:31:45 +02:00
1 parent 803705f795
commit fd36838cf3
10 files changed
+289 -4

No files matched your search

+22
View File
@@ -7444,6 +7444,8 @@ enabled = true
max_concurrent_threads_per_session = 5
usage_hint_enabled = false
usage_hint_text = "Custom delegation guidance."
root_agent_usage_hint_text = "Root guidance."
subagent_usage_hint_text = "Subagent guidance."
hide_spawn_agent_metadata = true
"#,
)?;
@@ -7462,6 +7464,14 @@ hide_spawn_agent_metadata = true
config.multi_agent_v2.usage_hint_text.as_deref(),
Some("Custom delegation guidance.")
);
assert_eq!(
config.multi_agent_v2.root_agent_usage_hint_text.as_deref(),
Some("Root guidance.")
);
assert_eq!(
config.multi_agent_v2.subagent_usage_hint_text.as_deref(),
Some("Subagent guidance.")
);
assert!(config.multi_agent_v2.hide_spawn_agent_metadata);
Ok(())
@@ -7478,12 +7488,16 @@ async fn profile_multi_agent_v2_config_overrides_base() -> std::io::Result<()> {
max_concurrent_threads_per_session = 4
usage_hint_enabled = true
usage_hint_text = "base hint"
root_agent_usage_hint_text = "base root hint"
subagent_usage_hint_text = "base subagent hint"
hide_spawn_agent_metadata = true
[profiles.no_hint.features.multi_agent_v2]
max_concurrent_threads_per_session = 6
usage_hint_enabled = false
usage_hint_text = "profile hint"
root_agent_usage_hint_text = "profile root hint"
subagent_usage_hint_text = "profile subagent hint"
hide_spawn_agent_metadata = false
"#,
)?;
@@ -7500,6 +7514,14 @@ hide_spawn_agent_metadata = false
config.multi_agent_v2.usage_hint_text.as_deref(),
Some("profile hint")
);
assert_eq!(
config.multi_agent_v2.root_agent_usage_hint_text.as_deref(),
Some("profile root hint")
);
assert_eq!(
config.multi_agent_v2.subagent_usage_hint_text.as_deref(),
Some("profile subagent hint")
);
assert!(!config.multi_agent_v2.hide_spawn_agent_metadata);
Ok(())
+16
View File
@@ -731,6 +731,8 @@ pub struct MultiAgentV2Config {
pub max_concurrent_threads_per_session: usize,
pub usage_hint_enabled: bool,
pub usage_hint_text: Option<String>,
pub root_agent_usage_hint_text: Option<String>,
pub subagent_usage_hint_text: Option<String>,
pub hide_spawn_agent_metadata: bool,
}
@@ -741,6 +743,8 @@ impl Default for MultiAgentV2Config {
DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION,
usage_hint_enabled: true,
usage_hint_text: None,
root_agent_usage_hint_text: None,
subagent_usage_hint_text: None,
hide_spawn_agent_metadata: false,
}
}
@@ -1619,6 +1623,16 @@ fn resolve_multi_agent_v2_config(
.or_else(|| base.and_then(|config| config.usage_hint_text.as_ref()))
.cloned()
.or(default.usage_hint_text);
let root_agent_usage_hint_text = profile
.and_then(|config| config.root_agent_usage_hint_text.as_ref())
.or_else(|| base.and_then(|config| config.root_agent_usage_hint_text.as_ref()))
.cloned()
.or(default.root_agent_usage_hint_text);
let subagent_usage_hint_text = profile
.and_then(|config| config.subagent_usage_hint_text.as_ref())
.or_else(|| base.and_then(|config| config.subagent_usage_hint_text.as_ref()))
.cloned()
.or(default.subagent_usage_hint_text);
let hide_spawn_agent_metadata = profile
.and_then(|config| config.hide_spawn_agent_metadata)
.or_else(|| base.and_then(|config| config.hide_spawn_agent_metadata))
@@ -1628,6 +1642,8 @@ fn resolve_multi_agent_v2_config(
max_concurrent_threads_per_session,
usage_hint_enabled,
usage_hint_text,
root_agent_usage_hint_text,
subagent_usage_hint_text,
hide_spawn_agent_metadata,
}
}