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,3 +1,4 @@
use crate::StartThreadOptions;
use crate::ThreadManager;
use crate::agent::AgentControl;
use crate::codex_thread::CodexThread;
@@ -33,7 +34,7 @@ async fn residency_slot_reservation_unloads_oldest_idle_v2_agent() {
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
);
let root = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start root thread");
let control = manager.agent_control();
@@ -79,7 +80,7 @@ async fn interrupted_v2_agent_is_lost_after_residency_eviction() {
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
);
let root = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start root thread");
let control = manager.agent_control();
+17 -45
View File
@@ -36,7 +36,6 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::CompactedItem;
use codex_protocol::protocol::ErrorEvent;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::InitialHistory;
use codex_protocol::protocol::InterAgentCommunication;
use codex_protocol::protocol::ItemCompletedEvent;
use codex_protocol::protocol::RolloutItem;
@@ -160,7 +159,7 @@ impl AgentControlHarness {
async fn start_thread(&self) -> (ThreadId, Arc<CodexThread>) {
let new_thread = self
.manager
.start_thread(self.config.clone())
.start_thread(StartThreadOptions::new(self.config.clone()))
.await
.expect("start thread");
(new_thread.thread_id, new_thread.thread)
@@ -169,19 +168,10 @@ impl AgentControlHarness {
async fn start_paginated_thread(&self) -> (ThreadId, Arc<CodexThread>) {
let new_thread = self
.manager
.start_thread_with_options(StartThreadOptions {
config: self.config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
.start_thread(StartThreadOptions {
history_mode: Some(ThreadHistoryMode::Paginated),
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: ExtensionDataInit::default(),
supports_openai_form_elicitation: false,
environments: Some(Vec::new()),
..StartThreadOptions::new(self.config.clone())
})
.await
.expect("start paginated thread");
@@ -1219,7 +1209,7 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
Some("Child subagent guidance.".to_string());
let new_thread = harness
.manager
.start_thread(parent_config.clone())
.start_thread(StartThreadOptions::new(parent_config.clone()))
.await
.expect("start parent thread");
let parent_thread_id = new_thread.thread_id;
@@ -1445,7 +1435,7 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history() {
Some("Child subagent guidance.".to_string());
let new_thread = harness
.manager
.start_thread(parent_config)
.start_thread(StartThreadOptions::new(parent_config))
.await
.expect("start parent thread");
let parent_thread_id = new_thread.thread_id;
@@ -1750,19 +1740,10 @@ async fn spawn_agent_fork_last_n_turns_drops_parent_startup_prefix_when_under_li
thread_extension_init.insert(selected_capability_roots.clone());
let parent = harness
.manager
.start_thread_with_options(StartThreadOptions {
config: harness.config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
.start_thread(StartThreadOptions {
environments: Some(Vec::new()),
thread_extension_init,
supports_openai_form_elicitation: false,
..StartThreadOptions::new(harness.config.clone())
})
.await
.expect("start parent thread");
@@ -1876,7 +1857,7 @@ async fn spawn_agent_fork_last_n_turns_strips_parent_usage_hints() {
Some("Child subagent guidance.".to_string());
let new_thread = harness
.manager
.start_thread(parent_config)
.start_thread(StartThreadOptions::new(parent_config))
.await
.expect("start parent thread");
let parent_thread_id = new_thread.thread_id;
@@ -1976,7 +1957,7 @@ async fn spawn_agent_respects_legacy_max_threads_alias() {
let control = manager.agent_control();
let _ = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread");
@@ -2227,7 +2208,7 @@ async fn multi_agent_v2_completion_ignores_dead_direct_parent() {
let _ = config.features.enable(Feature::MultiAgentV2);
let root = harness
.manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("root thread should start");
let root_thread_id = root.thread_id;
@@ -2339,7 +2320,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
let _ = tester_config.features.enable(Feature::MultiAgentV2);
let tester_thread_id = harness
.manager
.start_thread(tester_config.clone())
.start_thread(StartThreadOptions::new(tester_config.clone()))
.await
.expect("tester thread should start")
.thread_id;
@@ -2521,19 +2502,10 @@ async fn spawn_thread_subagents_persist_parent_originator_across_new_and_truncat
let harness = AgentControlHarness::new().await;
let parent = harness
.manager
.start_thread_with_options(StartThreadOptions {
config: harness.config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
.start_thread(StartThreadOptions {
metrics_service_name: Some("codex_work_desktop".to_string()),
parent_trace: None,
environments: Vec::new(),
thread_extension_init: ExtensionDataInit::default(),
supports_openai_form_elicitation: false,
environments: Some(Vec::new()),
..StartThreadOptions::new(harness.config.clone())
})
.await
.expect("parent thread should start");
@@ -3044,7 +3016,7 @@ async fn list_agent_subtree_thread_ids_finds_live_descendants_of_unloaded_root()
);
let control = manager.agent_control();
let parent_thread_id = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("parent should start")
.thread_id;
+4 -1
View File
@@ -17,6 +17,7 @@ use crate::session::session::Session;
use crate::session::turn::build_prompt;
use crate::session::turn::built_tools;
use crate::state_db_bridge::StateDbHandle;
use crate::thread_manager::StartThreadOptions;
use crate::thread_manager::ThreadManager;
use crate::thread_manager::thread_store_from_config;
use codex_extension_api::empty_extension_registry;
@@ -64,7 +65,9 @@ pub async fn build_prompt_input(
/*attestation_provider*/ None,
/*external_time_provider*/ None,
);
let thread = thread_manager.start_thread(config).await?;
let thread = thread_manager
.start_thread(StartThreadOptions::new(config))
.await?;
let output = build_prompt_input_from_session(&thread.thread.session, input).await;
let shutdown = thread.thread.shutdown_and_wait().await;
+32 -43
View File
@@ -198,11 +198,30 @@ pub struct StartThreadOptions {
pub dynamic_tools: Vec<codex_protocol::dynamic_tools::DynamicToolSpec>,
pub metrics_service_name: Option<String>,
pub parent_trace: Option<W3cTraceContext>,
pub environments: Vec<TurnEnvironmentSelection>,
pub environments: Option<Vec<TurnEnvironmentSelection>>,
pub thread_extension_init: ExtensionDataInit,
pub supports_openai_form_elicitation: bool,
}
impl StartThreadOptions {
pub fn new(config: Config) -> Self {
Self {
config,
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: None,
thread_extension_init: ExtensionDataInit::default(),
supports_openai_form_elicitation: false,
}
}
}
fn originator_from_service_name(service_name: Option<&str>) -> Option<String> {
let service_name = service_name?.trim();
for originator in [
@@ -662,52 +681,22 @@ impl ThreadManager {
Ok(subtree_thread_ids)
}
pub async fn start_thread(&self, config: Config) -> CodexResult<NewThread> {
// Box delegated thread-spawn futures so these convenience wrappers do
// not inline the full spawn path into every caller's async state.
Box::pin(self.start_thread_with_tools(config, Vec::new())).await
pub async fn start_thread(&self, options: StartThreadOptions) -> CodexResult<NewThread> {
Box::pin(self.start_thread_inner(options, /*forked_from_thread_id*/ None)).await
}
pub async fn start_thread_with_tools(
&self,
config: Config,
dynamic_tools: Vec<codex_protocol::dynamic_tools::DynamicToolSpec>,
) -> CodexResult<NewThread> {
let environments = default_thread_environment_selections(
self.state.environment_manager.as_ref(),
&config.cwd,
&config.workspace_roots,
);
Box::pin(self.start_thread_with_options(StartThreadOptions {
config,
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools,
metrics_service_name: None,
parent_trace: None,
environments,
thread_extension_init: ExtensionDataInit::default(),
supports_openai_form_elicitation: false,
}))
.await
}
pub async fn start_thread_with_options(
&self,
options: StartThreadOptions,
) -> CodexResult<NewThread> {
self.start_thread_with_options_and_fork_source(options, /*forked_from_thread_id*/ None)
.await
}
async fn start_thread_with_options_and_fork_source(
async fn start_thread_inner(
&self,
options: StartThreadOptions,
forked_from_thread_id: Option<ThreadId>,
) -> CodexResult<NewThread> {
let environments = options.environments.unwrap_or_else(|| {
default_thread_environment_selections(
self.state.environment_manager.as_ref(),
&options.config.cwd,
&options.config.workspace_roots,
)
});
let agent_control = self.agent_control_for_config(&options.config);
let (resumed_session_source, resumed_thread_source) = options
.initial_history
@@ -731,7 +720,7 @@ impl ThreadManager {
/*inherited_environments*/ None,
/*inherited_exec_policy*/ None,
options.parent_trace,
options.environments,
environments,
options.thread_extension_init,
options.supports_openai_form_elicitation,
/*user_shell_override*/ None,
@@ -772,7 +761,7 @@ impl ThreadManager {
inherited_multi_agent_version,
),
);
self.start_thread_with_options_and_fork_source(options, Some(forked_from_thread_id))
self.start_thread_inner(options, Some(forked_from_thread_id))
.await
}
+26 -80
View File
@@ -398,12 +398,12 @@ async fn shutdown_all_threads_bounded_submits_shutdown_to_every_thread() {
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
);
let thread_1 = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start first thread")
.thread_id;
let thread_2 = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start second thread")
.thread_id;
@@ -435,11 +435,11 @@ async fn code_mode_session_provider_is_shared_across_threads() {
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
);
let first = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start first thread");
let second = manager
.start_thread(config)
.start_thread(StartThreadOptions::new(config))
.await
.expect("start second thread");
@@ -491,21 +491,12 @@ async fn start_thread_keeps_internal_threads_hidden_from_normal_lookups() {
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
);
let thread = manager
.start_thread_with_options(StartThreadOptions {
config,
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
.start_thread(StartThreadOptions {
session_source: Some(SessionSource::Internal(
InternalSessionSource::MemoryConsolidation,
)),
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: Default::default(),
supports_openai_form_elicitation: false,
environments: Some(Vec::new()),
..StartThreadOptions::new(config)
})
.await
.expect("internal thread should start");
@@ -644,36 +635,19 @@ async fn start_thread_seeds_extension_data_for_mcp_and_lifecycle_contributors()
};
let first_thread = manager
.start_thread_with_options(StartThreadOptions {
config: config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
.start_thread(StartThreadOptions {
metrics_service_name: Some("codex_work_desktop".to_string()),
parent_trace: None,
environments: Vec::new(),
environments: Some(Vec::new()),
thread_extension_init: selected_root_init("selected-a", "env-a"),
supports_openai_form_elicitation: false,
..StartThreadOptions::new(config.clone())
})
.await
.expect("start first thread");
let second_thread = manager
.start_thread_with_options(StartThreadOptions {
config: config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
.start_thread(StartThreadOptions {
environments: Some(Vec::new()),
thread_extension_init: selected_root_init("selected-b", "env-b"),
supports_openai_form_elicitation: false,
..StartThreadOptions::new(config.clone())
})
.await
.expect("start second thread");
@@ -783,9 +757,7 @@ async fn selected_capability_roots_round_trip_through_fork() {
},
}];
let inherited = manager
.start_thread_with_options(StartThreadOptions {
config,
allow_provider_model_fallback: false,
.start_thread(StartThreadOptions {
initial_history: InitialHistory::Forked(vec![RolloutItem::SessionMeta(
SessionMetaLine {
meta: SessionMeta {
@@ -795,15 +767,8 @@ async fn selected_capability_roots_round_trip_through_fork() {
git: None,
},
)]),
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: Default::default(),
supports_openai_form_elicitation: false,
environments: Some(Vec::new()),
..StartThreadOptions::new(config)
})
.await
.expect("start inherited fork");
@@ -866,19 +831,9 @@ async fn resume_and_fork_do_not_restore_thread_environments_from_rollout() {
let mut source_config = config.clone();
source_config.cwd = selected_cwd.clone();
let source = manager
.start_thread_with_options(StartThreadOptions {
config: source_config,
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: environments.clone(),
thread_extension_init: Default::default(),
supports_openai_form_elicitation: false,
.start_thread(StartThreadOptions {
environments: Some(environments.clone()),
..StartThreadOptions::new(source_config)
})
.await
.expect("start source thread");
@@ -999,7 +954,7 @@ async fn explicit_installation_id_skips_codex_home_file() {
);
let thread = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start thread with explicit installation id");
@@ -1042,7 +997,7 @@ async fn resume_active_thread_from_rollout_returns_running_thread() {
);
let source = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start source thread");
source.thread.ensure_rollout_materialized().await;
@@ -1104,7 +1059,7 @@ async fn resume_stopped_thread_from_rollout_spawns_new_thread() {
);
let source = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start source thread");
source.thread.ensure_rollout_materialized().await;
@@ -1173,19 +1128,10 @@ async fn resume_stopped_thread_from_rollout_preserves_thread_source() {
);
let source = manager
.start_thread_with_options(StartThreadOptions {
config: config.clone(),
allow_provider_model_fallback: false,
initial_history: InitialHistory::New,
history_mode: None,
session_source: None,
.start_thread(StartThreadOptions {
thread_source: Some(ThreadSource::User),
dynamic_tools: Vec::new(),
metrics_service_name: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: Default::default(),
supports_openai_form_elicitation: false,
environments: Some(Vec::new()),
..StartThreadOptions::new(config.clone())
})
.await
.expect("start source thread");
@@ -1314,7 +1260,7 @@ async fn rollout_path_resume_and_fork_read_history_through_thread_store() {
);
let source = manager
.start_thread(config.clone())
.start_thread(StartThreadOptions::new(config.clone()))
.await
.expect("start source thread");
source
@@ -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();