diff --git a/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs b/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs index cab6b5253c..ca54c8dc3f 100644 --- a/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs +++ b/codex-rs/app-server/tests/suite/v2/multi_agent_v2_developer_instructions.rs @@ -53,7 +53,6 @@ const ROLE_INSTRUCTIONS: &str = "configured role developer instructions"; /// V2 fork modes, roles, and unset/blank overrides expose their agreed instruction precedence. #[test_case("no history"; "no history")] #[test_case("full history"; "full history")] -#[test_case("bounded history"; "bounded history")] #[test_case("configured role without instructions"; "configured role without instructions")] #[test_case("unset override"; "unset override")] #[test_case("blank override"; "blank override")] @@ -61,14 +60,12 @@ const ROLE_INSTRUCTIONS: &str = "configured role developer instructions"; #[test_case("explicit configured role"; "explicit configured role")] #[test_case("full history configured role"; "full history configured role")] #[test_case("implicit configured default"; "implicit configured default")] -#[test_case("bounded implicit configured default"; "bounded implicit configured default")] #[test_case("full fork skips default role"; "full fork skips default role")] #[tokio::test] async fn spawned_subagents_apply_configured_developer_instruction_precedence( case: &str, ) -> Result<()> { let fork_turns = match case { - "bounded history" | "bounded implicit configured default" => Some("1"), "no history" | "explicit configured role" | "implicit configured default" => Some("none"), _ => None, }; @@ -81,8 +78,7 @@ async fn spawned_subagents_apply_configured_developer_instruction_precedence( let configured_override = match case { "unset override" | "full history configured role" - | "configured role without instructions" - | "bounded implicit configured default" => None, + | "configured role without instructions" => None, "blank override" => Some(" "), "full history" => Some(" child-only developer instructions "), _ => Some(CHILD_INSTRUCTIONS), @@ -98,7 +94,6 @@ async fn spawned_subagents_apply_configured_developer_instruction_precedence( | "explicit configured role" | "full history configured role" | "implicit configured default" - | "bounded implicit configured default" | "full fork skips default role" ); let role_has_instructions = matches!( @@ -106,7 +101,6 @@ async fn spawned_subagents_apply_configured_developer_instruction_precedence( "explicit configured role" | "full history configured role" | "implicit configured default" - | "bounded implicit configured default" | "full fork skips default role" ); let expected = match case { @@ -114,8 +108,7 @@ async fn spawned_subagents_apply_configured_developer_instruction_precedence( "blank override" => None, "explicit configured role" | "full history configured role" - | "implicit configured default" - | "bounded implicit configured default" => Some(ROLE_INSTRUCTIONS), + | "implicit configured default" => Some(ROLE_INSTRUCTIONS), _ => Some(CHILD_INSTRUCTIONS), }; const PARENT_PROMPT: &str = "spawn the instruction override worker"; diff --git a/codex-rs/core/src/agent/control.rs b/codex-rs/core/src/agent/control.rs index a7af5100a8..903f178b17 100644 --- a/codex-rs/core/src/agent/control.rs +++ b/codex-rs/core/src/agent/control.rs @@ -19,7 +19,6 @@ use crate::thread_manager::ResumeThreadWithHistoryOptions; use crate::thread_manager::ThreadIdGenerator; use crate::thread_manager::ThreadManagerState; use crate::thread_manager::default_thread_id_generator; -use crate::thread_rollout_truncation::truncate_rollout_to_last_n_fork_turns; use crate::turn_timing::now_unix_timestamp_ms; use codex_history::InitialHistory; use codex_history::ResumedHistory; diff --git a/codex-rs/core/src/agent/control/spawn.rs b/codex-rs/core/src/agent/control/spawn.rs index db563dfe90..deef223bfc 100644 --- a/codex-rs/core/src/agent/control/spawn.rs +++ b/codex-rs/core/src/agent/control/spawn.rs @@ -7,7 +7,6 @@ use crate::agent::child_config::build_agent_resume_config; use crate::agent::role::apply_role_to_config; use crate::agent::types::AgentMetadata; use crate::agent::types::LiveAgent; -use crate::agent::types::SpawnAgentForkMode; use crate::agent::types::SpawnAgentOptions; use crate::agents_md_manager::SessionInstructions; use crate::codex_thread::CodexThread; @@ -122,8 +121,7 @@ fn keep_forked_rollout_item(item: &RolloutItem, preserve_context_baselines: bool | RolloutItem::RetainedContext(_) | RolloutItem::SecurityRiskScore(_) => false, // Full-history forks preserve the cached prompt prefix and can keep diffing - // from the parent's durable baseline. Truncated forks drop part of that prompt, - // so they must rebuild context on their first child turn. + // from the parent's durable baseline unless legacy compaction requires rebuilding it. RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) => preserve_context_baselines, // Child threads inherit model context, not the parent's cumulative usage state. RolloutItem::TokenUsageRecord(_) => false, @@ -140,7 +138,11 @@ fn retain_forked_developer_message(item: &mut ResponseItem, usage_hint_texts: &[ return false; }; content.retain(|content_item| { - if content_item.kind().0 == "guardian.approved_action" { + // Persisted role hints can predate the current bundled wording and lack markers. + if matches!( + content_item.kind().0.as_str(), + "guardian.approved_action" | "multi_agent.role_instructions" | "multi_agent.usage_hint" + ) { return false; } let ContentItem::InputText { text } = content_item.content() else { @@ -990,11 +992,11 @@ impl LocalAgentControl { "spawn_agent fork requires a parent spawn call id".to_string(), )); } - let Some(fork_mode) = options.fork_mode.as_ref() else { + if options.fork_mode.is_none() { return Err(CodexErr::Fatal( "spawn_agent fork requires a fork mode".to_string(), )); - }; + } let SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id, .. }) = &session_source @@ -1062,10 +1064,6 @@ impl LocalAgentControl { Some(meta_line.meta.selected_capability_roots.clone()) }) .unwrap_or_default(); - if let SpawnAgentForkMode::LastNTurns(last_n_turns) = fork_mode { - forked_rollout_items = - truncate_rollout_to_last_n_fork_turns(forked_rollout_items, *last_n_turns); - } let multi_agent_v2_usage_hint_texts_to_filter: Vec = if multi_agent_version == MultiAgentVersion::V2 { let parent_config = parent_thread.session.get_config().await; @@ -1082,19 +1080,17 @@ impl LocalAgentControl { } else { Vec::new() }; - let mut preserve_context_baselines = matches!(fork_mode, SpawnAgentForkMode::FullHistory); - if preserve_context_baselines { - for item in forked_rollout_items.iter().rev() { - let RolloutItem::Compacted(compacted) = item else { - continue; - }; - // Legacy checkpoints force the child to rebuild context regardless of the - // live parent's reference baseline; an older superseded checkpoint does not. - if compacted.replacement_history.is_none() { - preserve_context_baselines = false; - } - break; + let mut preserve_context_baselines = true; + for item in forked_rollout_items.iter().rev() { + let RolloutItem::Compacted(compacted) = item else { + continue; + }; + // Legacy checkpoints force the child to rebuild context regardless of the + // live parent's reference baseline; an older superseded checkpoint does not. + if compacted.replacement_history.is_none() { + preserve_context_baselines = false; } + break; } let mut replaced_parent_developer_instructions = false; // Scrub inherited hints and replace only the parent's developer-instruction fragment. diff --git a/codex-rs/core/src/agent/control/spawn_telemetry.rs b/codex-rs/core/src/agent/control/spawn_telemetry.rs index 7b681ce9c7..1501d935d2 100644 --- a/codex-rs/core/src/agent/control/spawn_telemetry.rs +++ b/codex-rs/core/src/agent/control/spawn_telemetry.rs @@ -28,7 +28,6 @@ pub(super) fn record_spawn_success( let fork_mode = match fork_mode { None => "none", Some(SpawnAgentForkMode::FullHistory) => "all", - Some(SpawnAgentForkMode::LastNTurns(_)) => "last_n", }; let history_mode = match measurements.history_mode { ThreadHistoryMode::Legacy => "legacy", diff --git a/codex-rs/core/src/agent/control/spawn_telemetry_tests.rs b/codex-rs/core/src/agent/control/spawn_telemetry_tests.rs index 410ace28d9..2c44e6cc60 100644 --- a/codex-rs/core/src/agent/control/spawn_telemetry_tests.rs +++ b/codex-rs/core/src/agent/control/spawn_telemetry_tests.rs @@ -39,7 +39,7 @@ fn records_bounded_spawn_phase_metrics() { record_spawn_success( &telemetry, - Some(&SpawnAgentForkMode::LastNTurns(3)), + Some(&SpawnAgentForkMode::FullHistory), MultiAgentVersion::V2, SpawnMeasurements { history_mode: ThreadHistoryMode::Paginated, @@ -74,7 +74,7 @@ fn records_bounded_spawn_phase_metrics() { }) .collect::>(); assert_eq!(attributes.len(), 5); - assert_eq!(attributes["fork_mode"], "last_n"); + assert_eq!(attributes["fork_mode"], "all"); assert_eq!(attributes["history_mode"], "paginated"); assert_eq!(attributes["multi_agent_version"], "v2"); assert_eq!(attributes["product_sku"], "codex"); diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 81846759f8..a0a1df5458 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -31,7 +31,6 @@ use crate::thread_manager::ForkSnapshot; use crate::thread_manager::StartThreadOptions; use crate::tools::handlers::multi_agents_common::thread_spawn_source; use assert_matches::assert_matches; -use codex_extension_api::ExtensionDataInit; use codex_extension_api::Instructions; use codex_extension_api::LoadInstructionsFuture; use codex_extension_api::LoadedUserInstructions; @@ -46,8 +45,6 @@ use codex_login::AuthManager; use codex_login::CodexAuth; use codex_protocol::AgentPath; use codex_protocol::ResponseItemId; -use codex_protocol::capabilities::CapabilityRootLocation; -use codex_protocol::capabilities::SelectedCapabilityRoot; use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; @@ -2243,88 +2240,6 @@ async fn spawn_agent_fork_sanitizes_inherited_compaction_metadata() { ); } -#[tokio::test] -async fn spawn_agent_numeric_fork_from_compacted_paginated_parent_clamps_to_provable_turns() { - let harness = AgentControlHarness::new().await; - let (parent_thread_id, parent_thread) = harness.start_paginated_thread().await; - let parent_spawn_call_id = "spawn-call-paginated-numeric".to_string(); - parent_thread - .session - .persist_rollout_items(&[ - RolloutItem::Compacted(CompactedItem { - message: String::new(), - replacement_history: Some(vec![ - ResponseItem::Message { - id: None, - role: "user".to_string(), - content: vec![ContentItem::InputText { - text: "compacted summary".to_string(), - }], - phase: None, - internal_chat_message_metadata_passthrough: None, - } - .into(), - ]), - retained_context: None, - guardian_history: None, - mcp_resource_origins: None, - window_number: None, - first_window_id: None, - previous_window_id: None, - window_id: None, - compaction_response_id: None, - latest_token_usage_record: None, - resume_metadata: None, - }), - rollout_response_item(ResponseItem::Message { - id: None, - role: "user".to_string(), - content: vec![ContentItem::InputText { - text: "recent parent turn".to_string(), - }], - phase: None, - internal_chat_message_metadata_passthrough: None, - }), - rollout_response_item(spawn_agent_call(&parent_spawn_call_id)), - ]) - .await; - - let clamped_child_thread_id = harness - .spawn_anonymous_child( - parent_thread_id, - SpawnAgentOptions { - fork_parent_spawn_call_id: Some(parent_spawn_call_id), - fork_mode: Some(SpawnAgentForkMode::LastNTurns(2)), - ..Default::default() - }, - ) - .await; - let clamped_child_thread = harness - .manager - .get_thread(clamped_child_thread_id) - .await - .expect("clamped child thread should be registered"); - let clamped_history = clamped_child_thread.session.clone_history().await; - assert!( - history_contains_text(clamped_history.raw_items(), "recent parent turn"), - "clamped numeric fork should keep the provable recent turn" - ); - assert!( - !history_contains_text(clamped_history.raw_items(), "compacted summary"), - "clamped numeric fork should not expand into compacted parent context" - ); - - let _ = harness - .control - .shutdown_live_agent(clamped_child_thread_id) - .await - .expect("clamped child shutdown should submit"); - let _ = parent_thread - .submit(Op::Shutdown {}) - .await - .expect("parent shutdown should submit"); -} - #[tokio::test] async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() { let managed_fragment = "\nParent developer instructions.\n"; @@ -2708,8 +2623,10 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() { .expect("parent shutdown should submit"); } +#[test_case::test_case(true; "marked catalog hints")] +#[test_case::test_case(false; "unmarked bundled hints")] #[tokio::test] -async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() { +async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history(marked: bool) { let harness = AgentControlHarness::new().await; let mut parent_config = harness.config.clone(); let _ = parent_config.features.enable(Feature::MultiAgentV2); @@ -2738,7 +2655,7 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() { let parent_spawn_call_id = "spawn-call-compacted-usage-hints".to_string(); let catalog_role = |base: &str| MultiAgentRoleInstructions::Composed { base: base.to_string(), - marked: true, + marked, omit_update_plan_instructions: false, max_concurrency: 2, wait_agent_enabled: false, @@ -2768,10 +2685,15 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() { id: None, role: "developer".to_string(), content: vec![ContentItem::InputText { - text: "Parent root guidance.".to_string(), + text: "Previous parent root guidance.".to_string(), }], phase: None, - internal_chat_message_metadata_passthrough: None, + internal_chat_message_metadata_passthrough: Some( + InternalChatMessageMetadataPassthrough { + content_item_kinds: Some(vec![ContentItemKind("multi_agent.usage_hint".to_string())]), + ..Default::default() + }, + ), }, ResponseItem::Message { id: None, @@ -2922,7 +2844,7 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() { "forked child history should not inherit compacted parent agent messages" ); assert!( - !history_contains_text(history.raw_items(), "Parent root guidance."), + !history_contains_text(history.raw_items(), "Previous parent root guidance."), "forked child history should strip stale parent hints from compacted replacement history" ); assert!( @@ -3529,443 +3451,6 @@ async fn spawn_agent_fork_flushes_parent_rollout_before_loading_history() { .expect("parent shutdown should submit"); } -#[tokio::test] -async fn spawn_agent_fork_last_n_turns_keeps_only_recent_turns() { - let harness = AgentControlHarness::new().await; - let (parent_thread_id, parent_thread) = harness.start_thread().await; - let old_turn_context = parent_thread.session.new_default_turn().await; - parent_thread - .session - .record_conversation_items( - old_turn_context.as_ref(), - old_turn_context.model_info(), - &[user_message("old parent context")], - ) - .await; - let source = McpAttributionSource { - connector_id: None, - plugin_id: None, - server_name: "example".to_string(), - tool_name: "search".to_string(), - first_turn_id: old_turn_context.sub_id.clone(), - }; - parent_thread - .session - .services - .executed_tool_calls - .record_mcp_source(source.clone()); - parent_thread - .session - .record_conversation_items( - old_turn_context.as_ref(), - old_turn_context.model_info(), - &[assistant_message("old MCP result", /*phase*/ None)], - ) - .await; - let queued_communication = InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - "queued message".to_string(), - /*trigger_turn*/ false, - ); - let queued_turn_context = parent_thread.session.new_default_turn().await; - parent_thread - .session - .record_conversation_items( - queued_turn_context.as_ref(), - queued_turn_context.model_info(), - &[queued_communication.to_response_input_item().into()], - ) - .await; - - let triggered_communication = InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - "triggered context".to_string(), - /*trigger_turn*/ true, - ); - let triggered_turn_context = parent_thread.session.new_default_turn().await; - parent_thread - .session - .record_conversation_items( - triggered_turn_context.as_ref(), - triggered_turn_context.model_info(), - &[triggered_communication.to_response_input_item().into()], - ) - .await; - parent_thread - .inject_response_items(vec![user_message("current parent task")]) - .await - .expect("inject current parent task"); - let spawn_turn_context = parent_thread.session.new_default_turn().await; - let parent_spawn_call_id = "spawn-call-last-n".to_string(); - parent_thread - .session - .record_conversation_items( - spawn_turn_context.as_ref(), - spawn_turn_context.model_info(), - &[spawn_agent_call(&parent_spawn_call_id)], - ) - .await; - parent_thread - .session - .persist_rollout_items(&[RolloutItem::TurnContext( - spawn_turn_context.to_turn_context_item(), - )]) - .await; - parent_thread - .session - .ensure_rollout_materialized(PersistContext::Standard) - .await; - parent_thread - .session - .flush_rollout() - .await - .expect("parent rollout should flush"); - - let child_thread_id = harness - .control - .spawn_agent_with_metadata( - harness.config.clone(), - text_input("child task"), - SessionSource::SubAgent(SubAgentSource::ThreadSpawn { - parent_thread_id, - depth: 1, - agent_path: None, - agent_nickname: None, - agent_role: None, - }), - SpawnAgentOptions { - fork_parent_spawn_call_id: Some(parent_spawn_call_id.clone()), - fork_mode: Some(SpawnAgentForkMode::LastNTurns(2)), - ..Default::default() - }, - ) - .await - .expect("forked spawn should keep only the last two turns") - .thread_id; - - let child_thread = harness - .manager - .get_thread(child_thread_id) - .await - .expect("child thread should be registered"); - let history = child_thread.session.clone_history().await; - - assert!( - !history_contains_text(history.raw_items(), "old parent context"), - "forked child history should drop parent context outside the requested last-N turn window" - ); - assert!( - !history_contains_text(history.raw_items(), "old MCP result"), - "forked child history should drop the old result while retaining its attribution" - ); - assert!( - !history_contains_text(history.raw_items(), "queued message"), - "forked child history should drop queued inter-agent messages outside the requested last-N turn window" - ); - assert!( - !history_contains_text(history.raw_items(), "triggered context"), - "forked child history should filter assistant inter-agent messages even when they fall inside the requested last-N turn window" - ); - assert!( - history_contains_text(history.raw_items(), "current parent task"), - "forked child history should keep the parent user message from the requested last-N turn window" - ); - assert!( - child_thread - .session - .reference_context_item() - .await - .is_none(), - "last-N forked child should rebuild context after truncating the cached prefix" - ); - assert_eq!( - mcp_attribution_in_constructed_request(&child_thread).await, - McpAttribution { - status: McpAttributionStatus::Complete, - error_reason: None, - sources: vec![source], - } - ); - - let _ = harness - .control - .shutdown_live_agent(child_thread_id) - .await - .expect("child shutdown should submit"); - let _ = parent_thread - .submit(Op::Shutdown {}) - .await - .expect("parent shutdown should submit"); -} - -#[tokio::test] -async fn spawn_agent_fork_last_n_turns_drops_parent_startup_prefix_when_under_limit() { - let harness = AgentControlHarness::new().await; - let selected_capability_roots = vec![SelectedCapabilityRoot { - id: "demo@1".to_string(), - location: CapabilityRootLocation::Environment { - environment_id: "build".to_string(), - path: PathUri::parse("file:///plugins/demo").expect("plugin root URI"), - }, - }]; - let mut thread_extension_init = ExtensionDataInit::new(); - thread_extension_init.insert(selected_capability_roots.clone()); - let parent = harness - .manager - .start_thread(StartThreadOptions { - environments: Some(Vec::new()), - thread_extension_init, - ..StartThreadOptions::new(harness.config.clone()) - }) - .await - .expect("start parent thread"); - let parent_thread_id = parent.thread_id; - let parent_thread = parent.thread; - let startup_turn_context = parent_thread.session.new_default_turn().await; - parent_thread - .session - .record_conversation_items( - startup_turn_context.as_ref(), - startup_turn_context.model_info(), - &[ResponseItem::Message { - id: None, - role: "developer".to_string(), - content: vec![ContentItem::InputText { - text: "parent startup developer context".to_string(), - }], - phase: None, - internal_chat_message_metadata_passthrough: None, - }], - ) - .await; - parent_thread - .inject_response_items(vec![user_message("current parent task")]) - .await - .expect("inject current parent task"); - let spawn_turn_context = parent_thread.session.new_default_turn().await; - let parent_spawn_call_id = "spawn-call-last-n-under-limit".to_string(); - parent_thread - .session - .record_conversation_items( - spawn_turn_context.as_ref(), - spawn_turn_context.model_info(), - &[spawn_agent_call(&parent_spawn_call_id)], - ) - .await; - parent_thread - .session - .ensure_rollout_materialized(PersistContext::Standard) - .await; - parent_thread - .session - .flush_rollout() - .await - .expect("parent rollout should flush"); - - let child_thread_id = harness - .control - .spawn_agent_with_metadata( - harness.config.clone(), - text_input("child task"), - SessionSource::SubAgent(SubAgentSource::ThreadSpawn { - parent_thread_id, - depth: 1, - agent_path: None, - agent_nickname: None, - agent_role: None, - }), - SpawnAgentOptions { - fork_parent_spawn_call_id: Some(parent_spawn_call_id), - fork_mode: Some(SpawnAgentForkMode::LastNTurns(2)), - ..Default::default() - }, - ) - .await - .expect("bounded forked spawn should drop startup prefix") - .thread_id; - - let child_thread = harness - .manager - .get_thread(child_thread_id) - .await - .expect("child thread should be registered"); - let history = child_thread.session.clone_history().await; - assert!( - history_contains_text(history.raw_items(), "current parent task"), - "bounded fork should retain the requested recent parent turn" - ); - assert!( - !history_contains_text(history.raw_items(), "parent startup developer context"), - "bounded fork should drop parent startup context even when fewer turns exist than requested" - ); - assert_eq!( - &child_thread.session.services.selected_capability_roots, - &selected_capability_roots - ); - assert!( - child_thread - .session - .reference_context_item() - .await - .is_none(), - "bounded forked child should still rebuild context after truncating the cached prefix" - ); - - let _ = harness - .control - .shutdown_live_agent(child_thread_id) - .await - .expect("child shutdown should submit"); - let _ = parent_thread - .submit(Op::Shutdown {}) - .await - .expect("parent shutdown should submit"); -} - -#[tokio::test] -async fn spawn_agent_fork_last_n_turns_strips_parent_usage_hints() { - let persistent_fragment = - "\nParent persistent instructions.\n"; - let harness = AgentControlHarness::new().await; - let mut parent_config = harness.config.clone(); - let _ = parent_config.features.enable(Feature::MultiAgentV2); - parent_config.developer_instructions = Some("Parent developer instructions.".to_string()); - parent_config.multi_agent_v2.root_agent_usage_hint_text = - Some("Parent root guidance.".to_string()); - let mut child_config = harness.config.clone(); - let _ = child_config.features.enable(Feature::MultiAgentV2); - child_config.developer_instructions = Some("Child developer instructions.".to_string()); - child_config.multi_agent_v2.subagent_developer_instructions = - Some("Child developer instructions.".to_string()); - child_config.multi_agent_v2.subagent_usage_hint_text = - Some("Child subagent guidance.".to_string()); - let new_thread = harness - .manager - .start_thread(StartThreadOptions::new(parent_config)) - .await - .expect("start parent thread"); - let parent_thread_id = new_thread.thread_id; - let parent_thread = new_thread.thread; - parent_thread - .inject_response_items(vec![user_message("parent task")]) - .await - .expect("inject parent task"); - let turn_context = parent_thread.session.new_default_turn().await; - let parent_spawn_call_id = "spawn-call-last-n-usage-hints".to_string(); - parent_thread - .session - .record_conversation_items( - turn_context.as_ref(), - turn_context.model_info(), - &[ - ResponseItem::Message { - id: None, - role: "developer".to_string(), - content: vec![ContentItem::InputText { - text: "Parent root guidance.".to_string(), - }], - phase: None, - internal_chat_message_metadata_passthrough: None, - }, - ResponseItem::Message { - id: None, - role: "developer".to_string(), - content: vec![ - ContentItem::InputText { - text: "Parent developer instructions.".to_string(), - }, - ContentItem::InputText { - text: "Preserved bounded developer context.".to_string(), - }, - ContentItem::InputText { - text: persistent_fragment.to_string(), - }, - ], - phase: None, - internal_chat_message_metadata_passthrough: None, - }, - spawn_agent_call(&parent_spawn_call_id), - ], - ) - .await; - parent_thread - .session - .ensure_rollout_materialized(PersistContext::Standard) - .await; - parent_thread - .session - .flush_rollout() - .await - .expect("parent rollout should flush"); - - let child_thread_id = harness - .control - .spawn_agent_with_metadata( - child_config, - text_input("child task"), - SessionSource::SubAgent(SubAgentSource::ThreadSpawn { - parent_thread_id, - depth: 1, - agent_path: None, - agent_nickname: None, - agent_role: None, - }), - SpawnAgentOptions { - fork_parent_spawn_call_id: Some(parent_spawn_call_id), - fork_mode: Some(SpawnAgentForkMode::LastNTurns(2)), - ..Default::default() - }, - ) - .await - .expect("bounded forked spawn should sanitize parent usage hints") - .thread_id; - - let child_thread = harness - .manager - .get_thread(child_thread_id) - .await - .expect("child thread should be registered"); - let history = child_thread.session.clone_history().await; - assert!( - history_contains_text(history.raw_items(), "parent task"), - "bounded fork should retain the requested recent parent turn" - ); - assert!( - !history_contains_text(history.raw_items(), "Parent root guidance."), - "bounded fork should strip stale parent root hints before the child rebuilds startup context" - ); - assert!( - !history_contains_text(history.raw_items(), "Parent developer instructions."), - "bounded fork should remove parent instructions before the child rebuilds startup context" - ); - assert!( - !history_contains_text(history.raw_items(), "Child developer instructions."), - "bounded fork should not inject child instructions before its canonical context rebuild" - ); - assert!( - !history_contains_text(history.raw_items(), persistent_fragment), - "bounded fork should remove persistent instructions before rebuilding context for the child's effort" - ); - assert!( - history_contains_text(history.raw_items(), "Preserved bounded developer context."), - "bounded fork should preserve unrelated developer fragments" - ); - - let _ = harness - .control - .shutdown_live_agent(child_thread_id) - .await - .expect("child shutdown should submit"); - let _ = parent_thread - .submit(Op::Shutdown {}) - .await - .expect("parent shutdown should submit"); -} - #[tokio::test] async fn spawn_agent_respects_legacy_max_threads_alias() { let max_threads = 1usize; @@ -4537,7 +4022,7 @@ async fn spawn_thread_subagent_gets_random_nickname_in_session_source() { } #[tokio::test] -async fn spawn_thread_subagents_persist_parent_originator_across_new_and_truncated_fork() { +async fn spawn_thread_subagents_persist_parent_originator_across_new_and_full_fork() { let harness = AgentControlHarness::new().await; let parent = harness .manager @@ -4588,8 +4073,8 @@ async fn spawn_thread_subagents_persist_parent_originator_across_new_and_truncat agent_role: Some("explorer".to_string()), }), SpawnAgentOptions { - fork_parent_spawn_call_id: Some("spawn-call-last-n".to_string()), - fork_mode: Some(SpawnAgentForkMode::LastNTurns(1)), + fork_parent_spawn_call_id: Some("spawn-call-full-history".to_string()), + fork_mode: Some(SpawnAgentForkMode::FullHistory), ..Default::default() }, ) diff --git a/codex-rs/core/src/agent/types.rs b/codex-rs/core/src/agent/types.rs index 8d612d6beb..852a516713 100644 --- a/codex-rs/core/src/agent/types.rs +++ b/codex-rs/core/src/agent/types.rs @@ -21,7 +21,6 @@ pub struct AgentMetadata { #[derive(Clone, Debug, PartialEq, Eq)] pub enum SpawnAgentForkMode { FullHistory, - LastNTurns(usize), } #[derive(Clone, Debug, Default)] diff --git a/codex-rs/core/src/thread_rollout_truncation.rs b/codex-rs/core/src/thread_rollout_truncation.rs index 16f67a47b8..5dde1d139d 100644 --- a/codex-rs/core/src/thread_rollout_truncation.rs +++ b/codex-rs/core/src/thread_rollout_truncation.rs @@ -14,7 +14,6 @@ use codex_protocol::error::Result as CodexResult; use codex_protocol::items::TurnItem; use codex_protocol::models::ResponseItem; use codex_protocol::protocol::EventMsg; -use codex_protocol::protocol::InterAgentCommunication; pub(crate) fn initial_history_has_prior_user_turns(conversation_history: &InitialHistory) -> bool { conversation_history.scan_rollout_items(rollout_item_is_user_turn_boundary) @@ -65,72 +64,6 @@ pub(crate) fn user_message_positions_in_rollout(items: &[RolloutItem]) -> Vec Vec { - let mut rollback_turn_positions = Vec::new(); - let mut fork_turn_positions = Vec::new(); - for (idx, item) in items.iter().enumerate() { - match item { - RolloutItem::ResponseItem(item) => { - let has_delivery_metadata = matches!(&item.item, ResponseItem::AgentMessage { .. }) - && idx.checked_sub(1).is_some_and(|previous_idx| { - matches!( - items.get(previous_idx), - Some(RolloutItem::InterAgentCommunicationMetadata { .. }) - ) - }); - if is_user_turn_boundary(item) && !has_delivery_metadata { - rollback_turn_positions.push(idx); - } - if is_real_user_message_boundary(item) || is_trigger_turn_boundary(item) { - fork_turn_positions.push(idx); - } - } - RolloutItem::InterAgentCommunication(communication) => { - rollback_turn_positions.push(idx); - if communication.trigger_turn { - fork_turn_positions.push(idx); - } - } - RolloutItem::InterAgentCommunicationMetadata { trigger_turn } => { - rollback_turn_positions.push(idx); - if *trigger_turn { - fork_turn_positions.push(idx); - } - } - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(rollback)) => { - let num_turns = usize::try_from(rollback.num_turns).unwrap_or(usize::MAX); - if num_turns == 0 { - continue; - } - let Some(rollback_start_idx) = rollback_turn_positions - .len() - .checked_sub(num_turns) - .map(|rollback_start| rollback_turn_positions[rollback_start]) - .or_else(|| rollback_turn_positions.first().copied()) - else { - continue; - }; - let new_rollback_len = rollback_turn_positions.len().saturating_sub(num_turns); - rollback_turn_positions.truncate(new_rollback_len); - fork_turn_positions.retain(|position| *position < rollback_start_idx); - } - _ => {} - } - } - fork_turn_positions -} - /// Return a prefix of `items` obtained by cutting strictly before the nth user message. /// /// The boundary index is 0-based from the start of `items` (so `n_from_start = 0` returns @@ -259,47 +192,6 @@ pub fn truncate_rollout_before_turn_id( Ok(items) } -/// Return a suffix of `items` that keeps the last `n_from_end` fork turns. -/// -/// If fewer than or equal to `n_from_end` fork turns exist, this keeps from the first fork-turn -/// boundary and still drops pre-turn startup context. -pub(crate) fn truncate_rollout_to_last_n_fork_turns( - mut items: Vec, - n_from_end: usize, -) -> Vec { - if n_from_end == 0 { - return Vec::new(); - } - - let fork_turn_positions = fork_turn_positions_in_rollout(&items); - let Some(keep_idx) = fork_turn_positions - .len() - .checked_sub(n_from_end) - .map(|position| fork_turn_positions[position]) - .or_else(|| fork_turn_positions.first().copied()) - else { - return Vec::new(); - }; - items.split_off(keep_idx) -} - -fn is_real_user_message_boundary(item: &ResponseItem) -> bool { - matches!( - event_mapping::parse_turn_item(item), - Some(TurnItem::UserMessage(_)) - ) -} - -fn is_trigger_turn_boundary(item: &ResponseItem) -> bool { - let ResponseItem::Message { role, content, .. } = item else { - return false; - }; - - role == "assistant" - && InterAgentCommunication::from_message_content(content) - .is_some_and(|communication| communication.trigger_turn) -} - #[cfg(test)] #[path = "thread_rollout_truncation_tests.rs"] mod tests; diff --git a/codex-rs/core/src/thread_rollout_truncation_tests.rs b/codex-rs/core/src/thread_rollout_truncation_tests.rs index 68ec36cc48..9625f74677 100644 --- a/codex-rs/core/src/thread_rollout_truncation_tests.rs +++ b/codex-rs/core/src/thread_rollout_truncation_tests.rs @@ -2,12 +2,10 @@ use super::*; use crate::session::step_context::StepContext; use crate::session::tests::build_world_state_from_turn_context; use crate::session::tests::make_session_and_context; -use codex_protocol::AgentPath; use codex_protocol::ResponseItemId; use codex_protocol::error::CodexErrorDetails; use codex_protocol::models::ContentItem; use codex_protocol::models::ReasoningItemReasoningSummary; -use codex_protocol::protocol::InterAgentCommunication; use codex_protocol::protocol::ThreadRolledBackEvent; use codex_protocol::protocol::TurnCompleteEvent; use codex_protocol::protocol::TurnStartedEvent; @@ -43,39 +41,6 @@ fn assistant_msg(text: &str) -> ResponseItem { } } -fn developer_msg(text: &str) -> ResponseItem { - ResponseItem::Message { - id: None, - role: "developer".to_string(), - content: vec![ContentItem::InputText { - text: text.to_string(), - }], - phase: None, - internal_chat_message_metadata_passthrough: None, - } -} - -fn inter_agent_msg(text: &str, trigger_turn: bool) -> ResponseItem { - let communication = InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - text.to_string(), - trigger_turn, - ); - communication.to_response_input_item().into() -} - -fn inter_agent_communication(text: &str, trigger_turn: bool) -> RolloutItem { - RolloutItem::InterAgentCommunication(InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - text.to_string(), - trigger_turn, - )) -} - fn turn_started(turn_id: &str) -> RolloutItem { RolloutItem::EventMsg(EventMsg::TurnStarted(TurnStartedEvent { turn_id: turn_id.to_string(), @@ -393,220 +358,3 @@ async fn ignores_session_prefix_messages_when_truncating_rollout_from_start() { serde_json::to_value(&expected).unwrap() ); } - -#[test] -fn truncates_rollout_to_last_n_fork_turns_counts_trigger_turn_messages() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(assistant_msg("a1")), - response_item(inter_agent_msg( - "queued message", - /*trigger_turn*/ false, - )), - response_item(assistant_msg("a2")), - response_item(inter_agent_msg( - "triggered task", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a3")), - response_item(user_msg("u2")), - response_item(assistant_msg("a4")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 2); - let expected = rollout[4..].to_vec(); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&expected).unwrap() - ); -} - -#[test] -fn fork_turn_positions_use_inter_agent_delivery_metadata() { - let rollout = vec![ - response_item(user_msg("user task")), - inter_agent_communication("queued during user turn", /*trigger_turn*/ false), - response_item(assistant_msg("first answer")), - inter_agent_communication("follow-up task", /*trigger_turn*/ true), - response_item(assistant_msg("second answer")), - response_item(user_msg("next user task")), - ]; - - assert_eq!(fork_turn_positions_in_rollout(&rollout), vec![0, 3, 5]); -} - -#[test] -fn fork_turn_positions_use_canonical_agent_messages_and_delivery_metadata() { - let queued = InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - "queued during user turn".to_string(), - /*trigger_turn*/ false, - ); - let triggered = InterAgentCommunication::new( - AgentPath::root(), - AgentPath::try_from("/root/worker").expect("agent path"), - Vec::new(), - "follow-up task".to_string(), - /*trigger_turn*/ true, - ); - let mut rollout = vec![ - response_item(user_msg("user task")), - RolloutItem::InterAgentCommunicationMetadata { - trigger_turn: false, - }, - response_item(queued.to_model_input_item()), - response_item(assistant_msg("first answer")), - RolloutItem::InterAgentCommunicationMetadata { trigger_turn: true }, - response_item(triggered.to_model_input_item()), - response_item(assistant_msg("second answer")), - response_item(user_msg("next user task")), - ]; - - assert_eq!(fork_turn_positions_in_rollout(&rollout), vec![0, 4, 7]); - - rollout.insert( - 7, - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(ThreadRolledBackEvent { - num_turns: 1, - })), - ); - assert_eq!(fork_turn_positions_in_rollout(&rollout), vec![0, 8]); -} - -#[test] -fn truncates_rollout_to_last_n_fork_turns_drops_startup_prefix_even_when_under_limit() { - let rollout = vec![ - response_item(developer_msg("startup developer context")), - response_item(user_msg("current task")), - response_item(assistant_msg("answer")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 2); - let expected = rollout[1..].to_vec(); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&expected).unwrap() - ); -} - -#[test] -fn truncates_rollout_to_last_n_fork_turns_applies_thread_rollback_markers() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(assistant_msg("a1")), - response_item(inter_agent_msg( - "triggered task", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a2")), - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(ThreadRolledBackEvent { - num_turns: 1, - })), - response_item(user_msg("u2")), - response_item(assistant_msg("a3")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 2); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&rollout).unwrap() - ); -} - -#[test] -fn fork_turn_positions_ignore_zero_turn_rollback_markers() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(inter_agent_msg( - "triggered task", - /*trigger_turn*/ true, - )), - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(ThreadRolledBackEvent { - num_turns: 0, - })), - response_item(user_msg("u2")), - ]; - - assert_eq!(fork_turn_positions_in_rollout(&rollout), vec![0, 1, 3]); -} - -#[test] -fn truncates_rollout_to_last_n_fork_turns_discards_trigger_boundaries_in_rolled_back_suffix() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(user_msg("u2")), - response_item(inter_agent_msg( - "triggered task", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a1")), - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(ThreadRolledBackEvent { - num_turns: 1, - })), - response_item(user_msg("u3")), - response_item(assistant_msg("a2")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 2); - - let expected = rollout[1..].to_vec(); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&expected).unwrap() - ); -} - -#[test] -fn truncates_rollout_to_last_n_fork_turns_discards_rolled_back_assistant_instruction_turns() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(assistant_msg("a1")), - response_item(inter_agent_msg( - "triggered task 1", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a2")), - RolloutItem::EventMsg(EventMsg::ThreadRolledBack(ThreadRolledBackEvent { - num_turns: 1, - })), - response_item(inter_agent_msg( - "triggered task 2", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a3")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 1); - let expected = rollout[5..].to_vec(); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&expected).unwrap() - ); -} - -#[test] -fn truncates_rollout_to_last_n_fork_turns_keeps_full_rollout_when_n_is_large() { - let rollout = vec![ - response_item(user_msg("u1")), - response_item(assistant_msg("a1")), - response_item(inter_agent_msg( - "triggered task", - /*trigger_turn*/ true, - )), - response_item(assistant_msg("a2")), - ]; - - let truncated = truncate_rollout_to_last_n_fork_turns(rollout.clone(), /*n_from_end*/ 10); - - assert_eq!( - serde_json::to_value(&truncated).unwrap(), - serde_json::to_value(&rollout).unwrap() - ); -} diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index be17be61b3..2146fdf84c 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -92,7 +92,6 @@ pub(crate) fn record_collab_spawn_failure( let fork_mode = match fork_mode { None => "none", Some(SpawnAgentForkMode::FullHistory) => "all", - Some(SpawnAgentForkMode::LastNTurns(_)) => "last_n", }; let multi_agent_version = match multi_agent_version { MultiAgentVersion::Disabled => "disabled", diff --git a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs index 92bae000c5..edc9467b4d 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs @@ -664,8 +664,7 @@ fn spawn_agent_common_properties_v2(agent_type_description: &str) -> BTreeMap().is_ok() { return Ok(Some(SpawnAgentForkMode::FullHistory)); } - let last_n_turns = fork_turns.parse::().map_err(|_| { - FunctionCallError::RespondToModel( - "fork_turns must be `none`, `all`, or a positive integer string".to_string(), - ) - })?; - if last_n_turns == 0 { - return Err(FunctionCallError::RespondToModel( - "fork_turns must be `none`, `all`, or a positive integer string".to_string(), - )); - } - - Ok(Some(SpawnAgentForkMode::LastNTurns(last_n_turns))) + Err(FunctionCallError::RespondToModel( + "fork_turns must be `none` or `all`".to_string(), + )) } } diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__compact__compaction_cold_resume_after_completion.snap b/codex-rs/core/tests/suite/snapshots/all__suite__compact__compaction_cold_resume_after_completion.snap index c836a85c94..2477eb2974 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__compact__compaction_cold_resume_after_completion.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__compact__compaction_cold_resume_after_completion.snap @@ -28,7 +28,7 @@ Scenario: Real paginated mid-turn compaction and cold resume from the bounded su There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 03:message/developer: 04:message/user: @@ -58,7 +58,7 @@ Scenario: Real paginated mid-turn compaction and cold resume from the bounded su There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 03:message/developer: 04:message/user: @@ -92,7 +92,7 @@ Scenario: Real paginated mid-turn compaction and cold resume from the bounded su There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 03:message/developer: 04:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__agent_message_board_context.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__agent_message_board_context.snap index 6360883eb4..a3a28e2414 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__agent_message_board_context.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__agent_message_board_context.snap @@ -27,7 +27,7 @@ Scenario: An active agent posts a shared decision and fetches the text without a There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 03:message/developer: 04:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__remote__remote_board_tools.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__remote__remote_board_tools.snap index c088fe3860..f9c80a8425 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__remote__remote_board_tools.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__agent_message_board__remote__remote_board_tools.snap @@ -33,7 +33,7 @@ Scenario: Remote board tools and active-turn notifications share the existing se There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 05:message/developer: 06:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_active_environment_selection.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_active_environment_selection.snap index 795fc65bbb..2c899e4a82 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_active_environment_selection.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_active_environment_selection.snap @@ -6,7 +6,7 @@ Scenario: Astra starts in one workspace, switches for the next step of the activ ## Window 1 -- request 1 (turn) -- -00:additional_tools/developer (3; hash=6517f5ceb3554929): +00:additional_tools/developer (3; hash=62160ed28e692c35): - namespace/functions; hash=4261ffb241688b3d - custom/exec - function/wait @@ -14,7 +14,7 @@ Scenario: Astra starts in one workspace, switches for the next step of the activ - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -90,7 +90,7 @@ Scenario: Astra starts in one workspace, switches for the next step of the activ There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_async_question_and_answer.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_async_question_and_answer.snap index b9999f419a..815d076e4f 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_async_question_and_answer.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_async_question_and_answer.snap @@ -7,7 +7,7 @@ Scenario: Astra uses the catalog question schema to ask who a launch update is f ## Window 1 -- request 1 (request) -- -00:additional_tools/developer (3; hash=2b6cdd8f97e8814b): +00:additional_tools/developer (3; hash=f181967456befa67): - namespace/functions; hash=7e611b8ddd5621cf - custom/exec - function/wait @@ -15,7 +15,7 @@ Scenario: Astra uses the catalog question schema to ask who a launch update is f - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_code_mode_call_timing.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_code_mode_call_timing.snap index cc57f4f811..63afb9950a 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_code_mode_call_timing.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_code_mode_call_timing.snap @@ -6,7 +6,7 @@ Scenario: Astra receives host and handler timings on completed and failed code-m ## Window 1 -- request 1 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -14,7 +14,7 @@ Scenario: Astra receives host and handler timings on completed and failed code-m - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -77,7 +77,7 @@ Scenario: Astra receives host and handler timings on completed and failed code-m There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_disabled_executor_skills.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_disabled_executor_skills.snap index 12ae7112ba..ce4690a6d0 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_disabled_executor_skills.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_disabled_executor_skills.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (request) -- -00:additional_tools/developer (3; hash=91d402a1b9d55c34): +00:additional_tools/developer (3; hash=5e8370540461db70): - namespace/functions; hash=7d26e086c1dbba28 - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -109,7 +109,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_interrupts_response.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_interrupts_response.snap index 548b77a10d..b81315dda0 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_interrupts_response.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_interrupts_response.snap @@ -6,7 +6,7 @@ Scenario: Astra receives new user input after its unfinished response is interru ## Window 1 -- request 1 (request) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -14,7 +14,7 @@ Scenario: Astra receives new user input after its unfinished response is interru - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_yields_code_mode_cell.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_yields_code_mode_cell.snap index 4574331821..cb8ed17bf8 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_yields_code_mode_cell.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_input_yields_code_mode_cell.snap @@ -6,7 +6,7 @@ Scenario: Astra continues after new user input yields a running code cell, then ## Window 1 -- request 1 (turn) -- -00:additional_tools/developer (3; hash=42b12a95deaea38e): +00:additional_tools/developer (3; hash=69c8f596545517aa): - namespace/functions; hash=13a36c9bc9ada856 - custom/exec - function/wait @@ -14,7 +14,7 @@ Scenario: Astra continues after new user input yields a running code cell, then - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -90,7 +90,7 @@ Scenario: Astra continues after new user input yields a running code cell, then There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap index 877c62c5bf..29a6122a39 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -112,7 +112,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user[2]: @@ -187,7 +187,7 @@ Settings: ## Window 2 (after request 3: input diverged at item 02) Settings: same as window 1 -- request 4 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -195,7 +195,7 @@ Settings: same as window 1 - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -285,7 +285,7 @@ Settings: same as window 1 There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 05:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 06:message/user[2]: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_plugin_refresh.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_plugin_refresh.snap index 878f9c13e3..5f76c13704 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_plugin_refresh.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_plugin_refresh.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap index 755867034a..ed6a215e26 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_catalog_messages.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_catalog_messages.snap index 9eeace188a..bcf207e89d 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_catalog_messages.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_catalog_messages.snap @@ -15,14 +15,14 @@ Settings: stream: true text: {"verbosity":"low"} tool_choice: "auto" - tools (7; hash=7a45a45148e7ea6a): + tools (7; hash=7916dd640b46ec6e): - custom/exec: Catalog exec instructions. Default yield: 1234 ms. `image(imageUrlOrItem: string | { image_url: s...; hash=a49b91fd82908e18 - function/wait: Catalog wait instructions: resume the yielded cell.; args=[cell_id, max_tokens, terminate, yield_time_ms]; hash=67bc0f04da3505b3 - function/request_user_input: Request user input for one to three short questions and wait for the response. This tool is only ...; args=[questions]; hash=64a729b25c424fc6 - function/request_user_input_async: Ask the user one or more questions during ongoing work. Use this tool only to request missing inf...; args=[questions]; hash=d6d74f33db330162 - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 03:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 04:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_settled_helpers__code_mode_settled_helpers.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_settled_helpers__code_mode_settled_helpers.snap index d33a16adea..f72b39e0b5 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_settled_helpers__code_mode_settled_helpers.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__code_mode_settled_helpers__code_mode_settled_helpers.snap @@ -6,7 +6,7 @@ Scenario: Settlement helpers deliver fulfilled and rejected records with input i ## Window 1 -- request 1 (turn) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -14,7 +14,7 @@ Scenario: Settlement helpers deliver fulfilled and rejected records with input i - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__compaction__remote_slash_compact.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__compaction__remote_slash_compact.snap index 00d8b90f33..4e78cadff5 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__compaction__remote_slash_compact.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__compaction__remote_slash_compact.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn; Before /compact) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: @@ -146,7 +146,7 @@ Settings: unavailable (items only) There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 02:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 03:message/user: @@ -164,7 +164,7 @@ Settings: unavailable (items only) ## Window 3 (after request 3: input diverged at item 00) Settings: same as window 1 -- request 4 (turn; First turn after /compact) -- -00:additional_tools/developer (3; hash=2c5a5b48dbcfce9b): +00:additional_tools/developer (3; hash=bf2e30605ff5dc17): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -172,7 +172,7 @@ Settings: same as window 1 - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -235,7 +235,7 @@ Settings: same as window 1 There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__guardian_code_mode_messaging.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__guardian_code_mode_messaging.snap index 5eea74ae35..0cbd8d0890 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__guardian_code_mode_messaging.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__guardian_code_mode_messaging.snap @@ -28,7 +28,7 @@ Scenario: A root uses Code Mode to send a confirmation and receives a real user There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 03:message/developer: 04:message/user: @@ -76,7 +76,7 @@ Scenario: A root uses Code Mode to send a confirmation and receives a real user There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...fork_turns` to `"none"` or a positive integer string. [hash=db3545796947884f] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...uctions; when doing so, set `fork_turns` to `"none"`. [hash=18ccf7e93ecabab9] 05:message/developer: 06:agent_message diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__indirect_namespace_prefixes__catalog_indirect_namespace_prefixes.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__indirect_namespace_prefixes__catalog_indirect_namespace_prefixes.snap index 6cacd42d3d..c7761cf293 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__indirect_namespace_prefixes__catalog_indirect_namespace_prefixes.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__indirect_namespace_prefixes__catalog_indirect_namespace_prefixes.snap @@ -15,8 +15,8 @@ Settings: stream: true text: {"verbosity":"low"} tool_choice: "auto" - tools (4; hash=a9bf6ab2694395ea): - - custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=046e4cb99a803db9 + tools (4; hash=7707adf4d5cde816): + - custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=66e0c24dd8b9010d - function/wait: Waits on a yielded `exec` cell and returns new output or completion. - Use `wait` only after `exe...; args=[cell_id, max_tokens, terminate, yield_time_ms]; hash=6692d8f6c55be489 - function/request_user_input: Request user input for one to three short questions and wait for the response. This tool is only ...; args=[questions]; hash=64a729b25c424fc6 - web_search; hash=e214d4db1bc49e89 @@ -49,8 +49,8 @@ Settings: ## Window 2 (after request 2) Settings: relative to window 1 model: "prefixes-cleared" - tools (4; hash=5aa602f8d0ed5702): - ~ custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=b1c19da7942f5c47 + tools (4; hash=d878b195101dccae): + ~ custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=881b443f5068d3fb -- request 3 (turn) -- 00:message/developer: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__mcp_resource_messages__mcp_resource_messages.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__mcp_resource_messages__mcp_resource_messages.snap index e3384c1269..2ec353f71d 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__mcp_resource_messages__mcp_resource_messages.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__mcp_resource_messages__mcp_resource_messages.snap @@ -15,14 +15,14 @@ Settings: stream: true text: {"verbosity":"low"} tool_choice: "auto" - tools (7; hash=221a8b190f48eeef): + tools (7; hash=dac6a853a6177003): - custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=9ad48dbb5eac1f9b - function/wait: Waits on a yielded `exec` cell and returns new output or completion. - Use `wait` only after `exe...; args=[cell_id, max_tokens, terminate, yield_time_ms]; hash=6692d8f6c55be489 - function/request_user_input: Request user input for one to three short questions and wait for the response. This tool is only ...; args=[questions]; hash=64a729b25c424fc6 - function/request_user_input_async: Ask the user one or more questions during ongoing work. Use this tool only to request missing inf...; args=[questions]; hash=d6d74f33db330162 - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 03:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 04:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__multi_agent_catalog_parameters.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__multi_agent_catalog_parameters.snap index 76ab79bd27..5fb3ec889e 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__multi_agent_catalog_parameters.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__multi_agent_catalog_parameters.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn) -- -00:additional_tools/developer (3; hash=cc5df79824bafc21): +00:additional_tools/developer (3; hash=2b84850bc279e5ed): - namespace/functions; hash=fada4a476a3754dd - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=170baa4086bfe3cb + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=7e156cf57f85af87 - function/followup_task - function/interrupt_agent - function/list_agents @@ -87,7 +87,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__provider_capabilities__custom_provider_capabilities.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__provider_capabilities__custom_provider_capabilities.snap index fd2c3135b2..ade1be5bcf 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__provider_capabilities__custom_provider_capabilities.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__provider_capabilities__custom_provider_capabilities.snap @@ -15,12 +15,12 @@ Settings: stream: true text: {"verbosity":"low"} tool_choice: "auto" - tools (6; hash=3b8fa9a9ca9788e3): + tools (6; hash=dde875513b2c7cff): - function/exec_command: Runs a command in a PTY, returning output or a session ID for ongoing interaction.; args=[cmd, justification, login, max_output_tokens, prefix_rule, sandbox_permissions, shell, tty, workdir, yield_time_ms]; hash=c04108e51a8548c5 - function/write_stdin: Writes characters to an existing unified exec session and returns recent output.; args=[chars, max_output_tokens, session_id, yield_time_ms]; hash=7fb62d8a3e2df4e2 - function/request_user_input: Request user input for one to three short questions and wait for the response. This tool is only ...; args=[questions]; hash=64a729b25c424fc6 - function/view_image: View a local image file from the filesystem when visual inspection is needed. Use this for images...; args=[detail, path]; hash=3b766d5bdec5d50e - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=3b8ac3e34b9c55bb + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=3cbf715dc154e1b7 - function/followup_task - function/interrupt_agent - function/list_agents diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__skill_catalog_dedup__dedup_before_budgeting.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__skill_catalog_dedup__dedup_before_budgeting.snap index 449e2a34b6..3dcc2d2c8a 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__skill_catalog_dedup__dedup_before_budgeting.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__skill_catalog_dedup__dedup_before_budgeting.snap @@ -16,7 +16,7 @@ Settings: text: {"verbosity":"low"} tool_choice: "auto" -- request 1 (turn) -- -00:additional_tools/developer (3; hash=b6ebf543280ec271): +00:additional_tools/developer (3; hash=e80da11342c6e6dd): - namespace/functions; hash=bd9e182b4235cdc1 - custom/exec - function/wait @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents @@ -108,7 +108,7 @@ Settings: There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. - Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`. [hash=d5928243dc643bcc] 04:message/developer: Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] 05:message/user: diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__strict_3p_cache__strict_3p_mcp_catalog_cache.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__strict_3p_cache__strict_3p_mcp_catalog_cache.snap index b6de242b41..a4dc574b7d 100644 --- a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__strict_3p_cache__strict_3p_mcp_catalog_cache.snap +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__strict_3p_cache__strict_3p_mcp_catalog_cache.snap @@ -24,7 +24,7 @@ Settings: - function/request_user_input_async - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 - function/sleep - - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=b263e3c84ad07c37 + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63 - function/followup_task - function/interrupt_agent - function/list_agents diff --git a/codex-rs/core/tests/suite/subagent_notifications.rs b/codex-rs/core/tests/suite/subagent_notifications.rs index 89deaae707..04b3026a50 100644 --- a/codex-rs/core/tests/suite/subagent_notifications.rs +++ b/codex-rs/core/tests/suite/subagent_notifications.rs @@ -1200,16 +1200,13 @@ async fn submit_turn_with_trigger(test: &TestCodex, prompt: &str, trigger: &str) #[derive(Clone, Copy, Debug)] enum GrandchildParentContext { FullHistory, - LastTurn, NoHistory, Compacted, } #[test_case(GrandchildParentContext::FullHistory, ThreadHistoryMode::Legacy; "legacy full history")] -#[test_case(GrandchildParentContext::LastTurn, ThreadHistoryMode::Legacy; "legacy last turn")] #[test_case(GrandchildParentContext::NoHistory, ThreadHistoryMode::Legacy; "legacy no history")] #[test_case(GrandchildParentContext::FullHistory, ThreadHistoryMode::Paginated; "paginated full history")] -#[test_case(GrandchildParentContext::LastTurn, ThreadHistoryMode::Paginated; "paginated last turn")] #[test_case(GrandchildParentContext::NoHistory, ThreadHistoryMode::Paginated; "paginated no history")] #[test_case(GrandchildParentContext::Compacted, ThreadHistoryMode::Legacy; "legacy full history after compaction")] #[test_case(GrandchildParentContext::Compacted, ThreadHistoryMode::Paginated; "paginated full history after compaction")] @@ -1233,7 +1230,6 @@ async fn grandchild_full_fork_preserves_context_baseline( let server = start_mock_server().await; let (parent_fork_turns, compact_parent) = match parent_context { GrandchildParentContext::FullHistory => ("all", false), - GrandchildParentContext::LastTurn => ("1", false), GrandchildParentContext::NoHistory => ("none", false), GrandchildParentContext::Compacted => ("all", true), }; @@ -1457,7 +1453,7 @@ async fn grandchild_full_fork_preserves_context_baseline( #[derive(Clone, Copy)] enum FullHistoryV2ModelSelection { ConfiguredDefault, - ExplicitOverride, + ExplicitOverride(&'static str), WorldStateIdentity, CurrentTimeReminders, MultiAgentModeInstructions, @@ -1465,7 +1461,9 @@ enum FullHistoryV2ModelSelection { } #[test_case(FullHistoryV2ModelSelection::ConfiguredDefault; "configured default with omitted fork_turns")] -#[test_case(FullHistoryV2ModelSelection::ExplicitOverride; "explicit override with fork_turns all")] +#[test_case(FullHistoryV2ModelSelection::ExplicitOverride("all"); "explicit override with fork_turns all")] +#[test_case(FullHistoryV2ModelSelection::ExplicitOverride("1"); "legacy one turn forks full history with explicit override")] +#[test_case(FullHistoryV2ModelSelection::ExplicitOverride("3"); "legacy three turns forks full history with explicit override")] #[test_case(FullHistoryV2ModelSelection::WorldStateIdentity; "world state appends context window when agent identity changes")] #[test_case(FullHistoryV2ModelSelection::CurrentTimeReminders; "full fork drops inherited current-time reminders")] #[test_case(FullHistoryV2ModelSelection::MultiAgentModeInstructions; "full fork drops inherited multi-agent mode instructions")] @@ -1500,11 +1498,11 @@ async fn spawned_full_history_v2_child_uses_model_precedence_without_dropping_co V2_DEFAULT_MODEL, V2_DEFAULT_REASONING_EFFORT, ), - FullHistoryV2ModelSelection::ExplicitOverride => ( + FullHistoryV2ModelSelection::ExplicitOverride(fork_turns) => ( json!({ "message": CHILD_PROMPT, "task_name": "worker", - "fork_turns": "all", + "fork_turns": fork_turns, "model": V2_REQUESTED_MODEL, "reasoning_effort": V2_REQUESTED_REASONING_EFFORT, }), @@ -1700,7 +1698,7 @@ async fn spawned_full_history_v2_child_uses_model_precedence_without_dropping_co builder = builder.with_external_time_provider(std::sync::Arc::new(FailFirstClockRead::default())); } - let test = builder.build(&server).await?; + let test = builder.build_with_auto_env(&server).await?; if matches!(selection, FullHistoryV2ModelSelection::WorldStateIdentity) { test.codex.submit(Op::Compact).await?; wait_for_event(&test.codex, |event| { diff --git a/codex-rs/prompts/src/multi_agent_instructions.rs b/codex-rs/prompts/src/multi_agent_instructions.rs index fea9405f26..415f52dd2a 100644 --- a/codex-rs/prompts/src/multi_agent_instructions.rs +++ b/codex-rs/prompts/src/multi_agent_instructions.rs @@ -5,7 +5,7 @@ use crate::without_update_plan_instructions; use codex_context_fragments::ContextualUserFragment; use codex_protocol::models::ContentItemKind; -const DEFAULT_MULTI_AGENT_V2_MODEL_OVERRIDE_USAGE_HINT_TEXT: &str = "Full-history forks (`fork_turns` omitted or `\"all\"`) inherit the parent model and reasoning effort and do not accept overrides. Only set `model` or `reasoning_effort` when explicitly requested by the user, applicable `AGENTS.md` instructions, or skill instructions; when doing so, set `fork_turns` to `\"none\"` or a positive integer string."; +const DEFAULT_MULTI_AGENT_V2_MODEL_OVERRIDE_USAGE_HINT_TEXT: &str = "Full-history forks (`fork_turns` omitted or `\"all\"`) inherit the parent model and reasoning effort and do not accept overrides. Only set `model` or `reasoning_effort` when explicitly requested by the user, applicable `AGENTS.md` instructions, or skill instructions; when doing so, set `fork_turns` to `\"none\"`."; const DEFAULT_MULTI_AGENT_V2_WAIT_AGENT_USAGE_HINT_TEXT: &str = "When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling."; const DEFAULT_MULTI_AGENT_V2_SHARED_USAGE_HINT_TEXT: &str = r#"Note that collaboration tools cannot be called from inside `functions.exec`. Call `spawn_agent`, `send_message`, `followup_task`, `wait_agent`, `interrupt_agent`, and `list_agents` only as direct tool calls using the recipient shown in their tool definitions, such as `to=functions.collaboration.spawn_agent`, since they are intentionally absent from the `functions.exec` `tools.*` namespace. Available tools in `functions.exec` are explicitly described with a `tools` namespace in the developer message.