mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
Drop parent setup context from forked agents
This commit is contained in:
@@ -127,6 +127,47 @@ fn keep_forked_rollout_item(item: &RolloutItem) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_parent_context_updates_from_forked_rollout(items: &mut Vec<RolloutItem>) {
|
||||
let mut drop_item = vec![false; items.len()];
|
||||
for idx in 0..items.len() {
|
||||
if !matches!(items[idx], RolloutItem::TurnContext(_)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drop_item[idx] = true;
|
||||
let mut context_idx = idx;
|
||||
while context_idx > 0 {
|
||||
let should_drop = match &items[context_idx - 1] {
|
||||
RolloutItem::ResponseItem(ResponseItem::Message { role, content, .. })
|
||||
if role == "developer" =>
|
||||
{
|
||||
true
|
||||
}
|
||||
RolloutItem::ResponseItem(ResponseItem::Message { role, content, .. })
|
||||
if role == "user"
|
||||
&& crate::event_mapping::is_contextual_user_message_content(content) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if !should_drop {
|
||||
break;
|
||||
}
|
||||
|
||||
context_idx -= 1;
|
||||
drop_item[context_idx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
items.retain(|_| {
|
||||
let keep = !drop_item[idx];
|
||||
idx += 1;
|
||||
keep
|
||||
});
|
||||
}
|
||||
|
||||
/// Control-plane handle for multi-agent operations.
|
||||
/// `AgentControl` is held by each session (via `SessionServices`). It provides capability to
|
||||
/// spawn new agents and the inter-agent communication layer.
|
||||
@@ -396,6 +437,10 @@ impl AgentControl {
|
||||
forked_rollout_items =
|
||||
truncate_rollout_to_last_n_fork_turns(&forked_rollout_items, *last_n_turns);
|
||||
}
|
||||
// Parent context updates are keyed by parent TurnContext snapshots. Forked children drop
|
||||
// those snapshots and build their own startup context, so inherited context messages would
|
||||
// otherwise be duplicated or stale in the child prompt.
|
||||
strip_parent_context_updates_from_forked_rollout(&mut forked_rollout_items);
|
||||
// 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.
|
||||
|
||||
@@ -608,6 +608,7 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
Some("Parent root guidance.".to_string());
|
||||
parent_config.multi_agent_v2.subagent_usage_hint_text =
|
||||
Some("Parent subagent guidance.".to_string());
|
||||
parent_config.developer_instructions = Some("Parent developer instructions.".to_string());
|
||||
let mut child_config = harness.config.clone();
|
||||
let _ = child_config.features.enable(Feature::MultiAgentV2);
|
||||
child_config.multi_agent_v2.root_agent_usage_hint_text =
|
||||
@@ -621,10 +622,15 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
.expect("start parent thread");
|
||||
let parent_thread_id = new_thread.thread_id;
|
||||
let parent_thread = new_thread.thread;
|
||||
let turn_context = parent_thread.codex.session.new_default_turn().await;
|
||||
parent_thread
|
||||
.codex
|
||||
.session
|
||||
.record_context_updates_and_set_reference_context_item(turn_context.as_ref())
|
||||
.await;
|
||||
parent_thread
|
||||
.inject_user_message_without_turn("parent seed context".to_string())
|
||||
.await;
|
||||
let turn_context = parent_thread.codex.session.new_default_turn().await;
|
||||
let parent_spawn_call_id = "spawn-call-history".to_string();
|
||||
let trigger_message = InterAgentCommunication::new(
|
||||
AgentPath::root(),
|
||||
@@ -639,22 +645,6 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
.record_conversation_items(
|
||||
turn_context.as_ref(),
|
||||
&[
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
role: "developer".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "Parent root guidance.".to_string(),
|
||||
}],
|
||||
phase: None,
|
||||
},
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
role: "developer".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "Parent subagent guidance.".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 +716,19 @@ 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 parent developer instructions from setup context"
|
||||
);
|
||||
assert!(
|
||||
!child_rollout.contains("Parent root guidance."),
|
||||
"forked child rollout should not retain parent multi-agent setup guidance"
|
||||
);
|
||||
|
||||
let expected = (
|
||||
child_thread_id,
|
||||
|
||||
Reference in New Issue
Block a user