Compare commits

...

1 Commits

Author SHA1 Message Date
Dylan Hurd
1ba0b75d1f Deduplicate forked subagent developer instructions 2026-05-16 02:12:45 -07:00
2 changed files with 27 additions and 5 deletions

View File

@@ -396,10 +396,9 @@ impl AgentControl {
forked_rollout_items =
truncate_rollout_to_last_n_fork_turns(&forked_rollout_items, *last_n_turns);
}
// MultiAgentV2 root/subagent usage hints are injected as standalone developer
// messages at thread start. When forking history, drop hints from the parent
// so the child gets a fresh hint that matches its own session source/config.
let multi_agent_v2_usage_hint_texts_to_filter: Vec<String> =
// Standalone developer items that the child reinjects at startup should not
// survive the inherited forked history, or they appear twice in the child prompt.
let mut forked_developer_texts_to_filter: Vec<String> =
if let Some(parent_thread) = parent_thread.as_ref() {
parent_thread
.codex
@@ -417,11 +416,16 @@ impl AgentControl {
} else {
Vec::new()
};
if let Some(developer_instructions) = config.developer_instructions.as_deref()
&& !developer_instructions.is_empty()
{
forked_developer_texts_to_filter.push(developer_instructions.to_string());
}
forked_rollout_items.retain(|item| {
if let RolloutItem::ResponseItem(ResponseItem::Message { role, content, .. }) = item
&& role == "developer"
&& let [ContentItem::InputText { text }] = content.as_slice()
&& multi_agent_v2_usage_hint_texts_to_filter
&& forked_developer_texts_to_filter
.iter()
.any(|usage_hint_text| usage_hint_text == text)
{

View File

@@ -614,6 +614,7 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
Some("Child root guidance.".to_string());
child_config.multi_agent_v2.subagent_usage_hint_text =
Some("Child subagent guidance.".to_string());
child_config.developer_instructions = Some("Parent developer instructions.".to_string());
let new_thread = harness
.manager
.start_thread(parent_config.clone())
@@ -655,6 +656,14 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
}],
phase: None,
},
ResponseItem::Message {
id: None,
role: "developer".to_string(),
content: vec![ContentItem::InputText {
text: "Parent developer instructions.".to_string(),
}],
phase: None,
},
assistant_message("parent commentary", Some(MessagePhase::Commentary)),
assistant_message("parent final answer", Some(MessagePhase::FinalAnswer)),
assistant_message("parent unknown phase", /*phase*/ None),
@@ -726,6 +735,15 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
&expected_history,
"forked child history should keep only parent user messages and assistant final answers"
);
let child_rollout_path = child_thread
.rollout_path()
.expect("forked child rollout path");
let child_rollout = std::fs::read_to_string(&child_rollout_path)
.expect("forked child rollout should be readable");
assert!(
!child_rollout.contains("Parent developer instructions."),
"forked child rollout should not retain developer instructions that will be reinjected"
);
let expected = (
child_thread_id,