Make thread store process-scoped (#19474)

- Build one app-server process ThreadStore from startup config and share
it with ThreadManager and CodexMessageProcessor.
- Remove per-thread/fork store reconstruction so effective thread config
cannot switch the persistence backend.
- Add params to ThreadStore create/resume for specifying thread
metadata, since otherwise the metadata from store creation would be used
(incorrectly).
This commit is contained in:
Tom
2026-04-30 21:24:59 -07:00
committed by GitHub
Unverified
parent f50c02d7bc
commit fe05acad23
55 changed files with 1076 additions and 514 deletions
@@ -5,7 +5,6 @@ use crate::config::DEFAULT_AGENT_MAX_DEPTH;
use crate::function_tool::FunctionCallError;
use crate::session::tests::make_session_and_context;
use crate::session_prefix::format_subagent_notification_message;
use crate::thread_manager::thread_store_from_config;
use crate::tools::context::ToolOutput;
use crate::tools::handlers::multi_agents_v2::CloseAgentHandler as CloseAgentHandlerV2;
use crate::tools::handlers::multi_agents_v2::FollowupTaskHandler as FollowupTaskHandlerV2;
@@ -297,10 +296,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -332,10 +328,7 @@ async fn spawn_agent_fork_context_rejects_child_model_overrides() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -370,10 +363,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -416,10 +406,7 @@ async fn multi_agent_v2_spawn_defaults_to_full_fork_and_rejects_child_model_over
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -460,10 +447,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -546,10 +530,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -583,10 +564,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -646,10 +624,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -746,10 +721,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -788,10 +760,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -830,10 +799,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -872,10 +838,7 @@ async fn multi_agent_v2_send_message_accepts_root_target_from_child() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -951,10 +914,7 @@ async fn multi_agent_v2_followup_task_rejects_root_target_from_child() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1035,10 +995,7 @@ async fn multi_agent_v2_list_agents_returns_completed_status_and_last_task_messa
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1132,10 +1089,7 @@ async fn multi_agent_v2_list_agents_filters_by_relative_path_prefix() {
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1222,10 +1176,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1289,10 +1240,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1348,10 +1296,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1424,10 +1369,7 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
let (mut session, mut turn) = make_session_and_context().await;
let manager = thread_manager();
let root = manager
.start_thread(
(*turn.config).clone(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1562,10 +1504,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1618,10 +1557,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1698,10 +1634,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1740,10 +1673,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -1957,7 +1887,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2082,7 +2012,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2124,7 +2054,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2219,7 +2149,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2260,7 +2190,6 @@ async fn resume_agent_restores_closed_agent_and_accepts_send_input() {
let thread = manager
.resume_thread_with_history(
config.clone(),
thread_store_from_config(&config),
InitialHistory::Forked(vec![RolloutItem::ResponseItem(ResponseItem::Message {
id: None,
role: "user".to_string(),
@@ -2425,10 +2354,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2605,7 +2531,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2648,7 +2574,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2686,7 +2612,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -2736,10 +2662,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2830,10 +2753,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -2911,10 +2831,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3002,10 +2919,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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3091,10 +3005,7 @@ async fn multi_agent_v2_close_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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3153,10 +3064,7 @@ async fn multi_agent_v2_close_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(),
thread_store_from_config(turn.config.as_ref()),
)
.start_thread((*turn.config).clone())
.await
.expect("root thread should start");
session.services.agent_control = manager.agent_control();
@@ -3206,7 +3114,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(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("start thread");
let agent_id = thread.thread_id;
@@ -3250,7 +3158,7 @@ async fn tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtr
.expect("test config should allow sqlite");
let parent = manager
.start_thread(config.clone(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("parent thread should start");
let parent_thread_id = parent.thread_id;
@@ -3381,7 +3289,7 @@ async fn tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtr
);
let operator = manager
.start_thread(config.clone(), thread_store_from_config(&config))
.start_thread(config.clone())
.await
.expect("operator thread should start");
let operator_session = operator.thread.codex.session.clone();