Consolidate thread startup around StartThreadOptions (#34814)

## What changed

- Add `StartThreadOptions::new` to provide the standard configuration for a new thread.
- Make `ThreadManager::start_thread` the single thread-start entry point and migrate callers from the previous convenience methods.
- Derive default environment selections when `environments` is `None`, while preserving explicit selections, including an empty list.

GitOrigin-RevId: 8977dc11aed54c5e1215a81eaed2b2cf5fc6087a
This commit is contained in:
pakrym-oai
2026-07-22 19:31:54 +00:00
committed by copyberry
parent 06782eded7
commit 08ae0fc0ce
25 changed files with 237 additions and 399 deletions
@@ -1,4 +1,5 @@
use super::*;
use crate::StartThreadOptions;
use crate::ThreadManager;
use crate::config::AgentRoleConfig;
use crate::config::DEFAULT_AGENT_MAX_DEPTH;
@@ -316,7 +317,7 @@ async fn spawn_agent_fork_context_rejects_agent_type_override() {
let role_name = install_role_with_model_override(&mut turn).await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -350,7 +351,7 @@ async fn multi_agent_v2_spawn_fork_turns_all_rejects_agent_type_override() {
let role_name = install_role_with_model_override(&mut turn).await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -436,7 +437,7 @@ async fn spawn_agent_service_tier_override_validates_the_effective_child_model()
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -541,7 +542,7 @@ async fn spawn_agent_service_tier_inheritance_preserves_supported_or_configured_
turn.config = Arc::new(config);
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -582,7 +583,7 @@ async fn spawn_agent_service_tier_inheritance_preserves_supported_or_configured_
turn.config = Arc::new(config);
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -645,7 +646,7 @@ service_tier = "priority"
turn.config = Arc::new(config);
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -718,7 +719,7 @@ service_tier = "turbo"
turn.config = Arc::new(config);
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -815,7 +816,7 @@ async fn spawn_agent_full_history_fork_accepts_explicit_service_tier() {
.await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -869,7 +870,7 @@ async fn multi_agent_v2_full_history_fork_accepts_explicit_service_tier() {
set_turn_config(&mut turn, config);
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -922,7 +923,7 @@ async fn multi_agent_v2_spawn_partial_fork_turns_allows_agent_type_override() {
let role_name = install_role_with_model_override(&mut turn).await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1006,7 +1007,7 @@ async fn multi_agent_v2_spawn_requires_task_name() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1040,7 +1041,7 @@ async fn multi_agent_v2_spawn_rejects_legacy_items_field() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1100,7 +1101,7 @@ async fn multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_pat
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1195,7 +1196,7 @@ async fn multi_agent_v2_spawn_rejects_legacy_fork_context() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1235,7 +1236,7 @@ async fn multi_agent_v2_spawn_rejects_invalid_fork_turns_string() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1275,7 +1276,7 @@ async fn multi_agent_v2_spawn_rejects_zero_fork_turns() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1321,7 +1322,7 @@ async fn multi_agent_v2_send_message_accepts_root_target_from_child() {
.expect("test config should allow feature update");
set_turn_config(&mut turn, config);
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1397,7 +1398,7 @@ async fn multi_agent_v2_followup_task_rejects_root_target_from_child() {
.expect("test config should allow feature update");
set_turn_config(&mut turn, config);
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1473,7 +1474,7 @@ async fn multi_agent_v2_list_agents_returns_completed_status() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1561,7 +1562,7 @@ async fn multi_agent_v2_list_agents_filters_by_relative_path_prefix() {
let _ = config.features.enable(Feature::MultiAgentV2);
set_turn_config(&mut turn, config.clone());
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1642,7 +1643,7 @@ async fn multi_agent_v2_list_agents_omits_closed_agents() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1702,7 +1703,7 @@ async fn multi_agent_v2_list_agents_keeps_interrupted_resident_agents() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1774,7 +1775,7 @@ async fn multi_agent_v2_send_message_rejects_legacy_items_field() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1830,7 +1831,7 @@ async fn multi_agent_v2_send_message_rejects_interrupt_parameter() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1907,7 +1908,7 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
let _ = config.features.enable(Feature::MultiAgentV2);
set_turn_config(&mut turn, config);
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
// Production spawn_agent calls happen after the parent turn has resolved
@@ -2060,7 +2061,7 @@ async fn multi_agent_v2_followup_task_rejects_legacy_items_field() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2113,7 +2114,7 @@ async fn multi_agent_v2_interrupted_turn_does_not_notify_parent() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2190,7 +2191,7 @@ async fn multi_agent_v2_spawn_omits_agent_id_when_named() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2229,7 +2230,7 @@ async fn multi_agent_v2_spawn_surfaces_task_name_validation_errors() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2449,7 +2450,7 @@ async fn multi_agent_v2_spawn_agent_ignores_configured_max_depth() {
.enable(Feature::MultiAgentV2)
.expect("test config should allow feature update");
let root = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2574,7 +2575,7 @@ async fn send_input_interrupts_before_prompt() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2616,7 +2617,7 @@ async fn send_input_accepts_structured_items() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2712,7 +2713,7 @@ async fn resume_agent_noops_for_active_agent() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2918,7 +2919,7 @@ async fn multi_agent_v2_wait_agent_accepts_timeout_only_argument() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3270,7 +3271,7 @@ async fn wait_agent_times_out_when_status_is_not_final() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -3313,7 +3314,7 @@ async fn wait_agent_clamps_short_timeouts_to_minimum() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -3351,7 +3352,7 @@ async fn wait_agent_returns_final_status_without_timeout() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -3401,7 +3402,7 @@ async fn multi_agent_v2_wait_agent_returns_summary_for_mailbox_activity() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3491,7 +3492,7 @@ async fn multi_agent_v2_wait_agent_returns_for_already_queued_mail() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3572,7 +3573,7 @@ async fn multi_agent_v2_wait_agent_wakes_on_any_mailbox_notification() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3663,7 +3664,7 @@ async fn multi_agent_v2_wait_agent_does_not_return_completed_content() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3752,7 +3753,7 @@ async fn multi_agent_v2_interrupt_agent_accepts_task_name_target() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3878,7 +3879,7 @@ async fn multi_agent_v2_interrupt_agent_accepts_unloaded_task_name_target() {
Some(state_db.clone()),
);
let root = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3969,7 +3970,7 @@ async fn multi_agent_v2_interrupt_agent_rejects_root_target_and_id() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -4025,7 +4026,7 @@ async fn multi_agent_v2_interrupt_agent_rejects_self_target_by_id() {
.expect("test config should allow feature update");
set_turn_config(&mut turn, config);
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -4092,7 +4093,7 @@ async fn multi_agent_v2_interrupt_agent_rejects_self_target_by_task_name() {
.expect("test config should allow feature update");
set_turn_config(&mut turn, config);
let root = manager
.start_thread((*turn.config).clone())
.start_thread(StartThreadOptions::new((*turn.config).clone()))
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -4155,7 +4156,7 @@ async fn close_agent_submits_shutdown_and_returns_previous_status() {
session.services.agent_control = manager.agent_control();
let config = turn.config.as_ref().clone();
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -4216,7 +4217,7 @@ async fn tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtr
);
let parent = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("parent thread should start");
let parent_thread_id = parent.thread_id;
@@ -4348,7 +4349,7 @@ async fn tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtr
);
let operator = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("operator thread should start");
let operator_session = operator.thread.session.clone();