Preserve context baselines for full-history agent forks (#23352)

## Why

Full-history agent forks should continue from the same prompt prefix as
the parent. Dropping the stored `TurnContext` baseline forced the child
to rebuild startup context on its first turn, which can duplicate
developer instructions and also loses the cache continuity that a
full-history fork is supposed to preserve.

Truncated forks are different: once we keep only the last N turns, the
original prompt prefix is no longer intact, so the child must establish
a fresh context baseline.

## What changed

- Preserve `RolloutItem::TurnContext` when forking with
`SpawnAgentForkMode::FullHistory`, and keep dropping it for truncated
forks:
https://github.com/openai/codex/blob/4090717d94c1fc7f33c9bd122be133a0c5752052/codex-rs/core/src/agent/control.rs#L98-L126
and
https://github.com/openai/codex/blob/4090717d94c1fc7f33c9bd122be133a0c5752052/codex-rs/core/src/agent/control.rs#L399-L401
- Remove the special-case MultiAgentV2 usage-hint filtering path.
Full-history fork now preserves the cached developer prefix instead of
trying to reconstruct part of it.
- Extend the fork coverage to assert both sides of the contract:
full-history forks keep the parent reference baseline, while last-N
forks rebuild context after truncation:
https://github.com/openai/codex/blob/4090717d94c1fc7f33c9bd122be133a0c5752052/codex-rs/core/src/agent/control_tests.rs#L603-L759
and
https://github.com/openai/codex/blob/4090717d94c1fc7f33c9bd122be133a0c5752052/codex-rs/core/src/agent/control_tests.rs#L854-L977

## Verification

- `cargo test -p codex-core
spawn_agent_can_fork_parent_thread_history_with_sanitized_items --
--nocapture`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core
spawn_agent_fork_last_n_turns_keeps_only_recent_turns -- --nocapture`
This commit is contained in:
jif-oai
2026-05-19 10:34:24 +02:00
committed by GitHub
parent 3009e23644
commit 826b2182ed
4 changed files with 332 additions and 75 deletions
-16
View File
@@ -884,22 +884,6 @@ impl Session {
}
}
pub(crate) async fn configured_multi_agent_v2_usage_hint_texts(&self) -> Vec<String> {
if !self.features.enabled(Feature::MultiAgentV2) {
return Vec::new();
}
let state = self.state.lock().await;
let config = &state.session_configuration.original_config_do_not_use;
[
config.multi_agent_v2.root_agent_usage_hint_text.clone(),
config.multi_agent_v2.subagent_usage_hint_text.clone(),
]
.into_iter()
.flatten()
.collect()
}
fn managed_network_proxy_active_for_permission_profile(
permission_profile: &PermissionProfile,
) -> bool {
-35
View File
@@ -22,7 +22,6 @@ use codex_config::loader::project_trust_key;
use codex_config::types::ToolSuggestDisabledTool;
use codex_features::Feature;
use codex_features::Features;
use codex_login::CodexAuth;
use codex_model_provider_info::ModelProviderInfo;
use codex_models_manager::bundled_models_response;
@@ -6745,40 +6744,6 @@ async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_feature_dis
);
}
#[tokio::test]
async fn configured_multi_agent_v2_usage_hint_texts_use_effective_enabled_feature_state() {
let (mut session, _turn_context) =
make_multi_agent_v2_usage_hint_test_session(/*enable_multi_agent_v2*/ false).await;
let mut effective_features = Features::with_defaults();
effective_features.enable(Feature::MultiAgentV2);
Arc::get_mut(&mut session)
.expect("session should not be shared")
.features = effective_features.into();
let hint_texts = session.configured_multi_agent_v2_usage_hint_texts().await;
assert_eq!(
hint_texts,
vec![
"Root guidance.".to_string(),
"Subagent guidance.".to_string()
]
);
}
#[tokio::test]
async fn configured_multi_agent_v2_usage_hint_texts_omit_effectively_disabled_feature() {
let (mut session, _turn_context) =
make_multi_agent_v2_usage_hint_test_session(/*enable_multi_agent_v2*/ true).await;
Arc::get_mut(&mut session)
.expect("session should not be shared")
.features = Features::with_defaults().into();
let hint_texts = session.configured_multi_agent_v2_usage_hint_texts().await;
assert_eq!(hint_texts, Vec::<String>::new());
}
#[tokio::test]
async fn build_initial_context_omits_default_image_save_location_with_image_history() {
let (session, turn_context) = make_session_and_context().await;