@@ -88,6 +88,7 @@ use codex_login::AuthManager;
8888use codex_protocol:: protocol:: SessionSource ;
8989pub use codex_rollout:: StateDbHandle ;
9090pub use codex_state:: log_db:: LogDbLayer ;
91+ use codex_thread_store:: ThreadStore ;
9192use tokio:: sync:: mpsc;
9293use tokio:: sync:: oneshot;
9394use tokio:: time:: timeout;
@@ -150,6 +151,23 @@ pub struct InProcessStartArgs {
150151 pub channel_capacity : usize ,
151152}
152153
154+ /// Optional dependencies and behavior overrides for an in-process runtime.
155+ ///
156+ /// Use [`InProcessStartOptions::default`] to preserve the standard app-server
157+ /// startup behavior.
158+ #[ derive( Default ) ]
159+ pub struct InProcessStartOptions {
160+ thread_store : Option < Arc < dyn ThreadStore > > ,
161+ }
162+
163+ impl InProcessStartOptions {
164+ /// Uses `thread_store` instead of the store derived from the runtime config.
165+ pub fn with_thread_store ( mut self , thread_store : Arc < dyn ThreadStore > ) -> Self {
166+ self . thread_store = Some ( thread_store) ;
167+ self
168+ }
169+ }
170+
153171/// Event emitted from the app-server to the in-process client.
154172///
155173/// [`Lagged`](Self::Lagged) is a transport health marker, not an application
@@ -350,8 +368,16 @@ impl InProcessClientHandle {
350368/// the handle, so callers receive a ready-to-use runtime. If initialize fails,
351369/// the runtime is shut down and an `InvalidData` error is returned.
352370pub async fn start ( args : InProcessStartArgs ) -> IoResult < InProcessClientHandle > {
371+ start_with_options ( args, InProcessStartOptions :: default ( ) ) . await
372+ }
373+
374+ /// Starts an in-process app-server runtime with explicit dependency overrides.
375+ pub async fn start_with_options (
376+ args : InProcessStartArgs ,
377+ options : InProcessStartOptions ,
378+ ) -> IoResult < InProcessClientHandle > {
353379 let initialize = args. initialize . clone ( ) ;
354- let client = start_uninitialized ( args) . await ?;
380+ let client = start_uninitialized ( args, options ) . await ?;
355381
356382 let initialize_response = client
357383 . request ( ClientRequest :: Initialize {
@@ -371,7 +397,10 @@ pub async fn start(args: InProcessStartArgs) -> IoResult<InProcessClientHandle>
371397 Ok ( client)
372398}
373399
374- async fn start_uninitialized ( args : InProcessStartArgs ) -> IoResult < InProcessClientHandle > {
400+ async fn start_uninitialized (
401+ args : InProcessStartArgs ,
402+ options : InProcessStartOptions ,
403+ ) -> IoResult < InProcessClientHandle > {
375404 let channel_capacity = args. channel_capacity . max ( 1 ) ;
376405 let installation_id = resolve_installation_id ( & args. config . codex_home ) . await ?;
377406 let ( client_tx, mut client_rx) = mpsc:: channel :: < InProcessClientMessage > ( channel_capacity) ;
@@ -440,6 +469,7 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult<InProcessClie
440469 rpc_transport : AppServerRpcTransport :: InProcess ,
441470 remote_control_handle : None ,
442471 plugin_startup_tasks : crate :: PluginStartupTasks :: Start ,
472+ thread_store : options. thread_store ,
443473 } ) ) ;
444474 let mut thread_created_rx = processor. thread_created_receiver ( ) ;
445475 let session = Arc :: new ( ConnectionSessionState :: new ( ) ) ;
@@ -733,13 +763,15 @@ mod tests {
733763 use codex_app_server_protocol:: ConfigRequirementsReadResponse ;
734764 use codex_app_server_protocol:: ExternalAgentConfigImportCompletedNotification ;
735765 use codex_app_server_protocol:: SessionSource as ApiSessionSource ;
766+ use codex_app_server_protocol:: ThreadListParams ;
736767 use codex_app_server_protocol:: ThreadStartParams ;
737768 use codex_app_server_protocol:: ThreadStartResponse ;
738769 use codex_app_server_protocol:: Turn ;
739770 use codex_app_server_protocol:: TurnCompletedNotification ;
740771 use codex_app_server_protocol:: TurnItemsView ;
741772 use codex_app_server_protocol:: TurnStatus ;
742773 use codex_core:: config:: ConfigBuilder ;
774+ use codex_thread_store:: InMemoryThreadStore ;
743775 use pretty_assertions:: assert_eq;
744776 use std:: path:: Path ;
745777 use tempfile:: TempDir ;
@@ -760,15 +792,12 @@ mod tests {
760792 }
761793 }
762794
763- async fn start_test_client_with_capacity (
795+ async fn build_test_start_args (
764796 session_source : SessionSource ,
765797 channel_capacity : usize ,
766- ) -> InProcessClientHandle {
798+ ) -> ( TempDir , InProcessStartArgs ) {
767799 let codex_home = TempDir :: new ( ) . expect ( "temp dir" ) ;
768800 let config = Arc :: new ( build_test_config ( codex_home. path ( ) ) . await ) ;
769- let state_db = codex_rollout:: state_db:: try_init ( config. as_ref ( ) )
770- . await
771- . expect ( "state db should initialize for in-process test" ) ;
772801 let args = InProcessStartArgs {
773802 arg0_paths : Arg0DispatchPaths :: default ( ) ,
774803 config,
@@ -779,7 +808,7 @@ mod tests {
779808 thread_config_loader : Arc :: new ( codex_config:: NoopThreadConfigLoader ) ,
780809 feedback : CodexFeedback :: new ( ) ,
781810 log_db : None ,
782- state_db : Some ( state_db ) ,
811+ state_db : None ,
783812 environment_manager : Arc :: new ( EnvironmentManager :: default_for_tests ( ) ) ,
784813 config_warnings : Vec :: new ( ) ,
785814 session_source,
@@ -794,6 +823,27 @@ mod tests {
794823 } ,
795824 channel_capacity,
796825 } ;
826+ ( codex_home, args)
827+ }
828+
829+ async fn start_test_client_with_capacity_and_options (
830+ session_source : SessionSource ,
831+ channel_capacity : usize ,
832+ options : InProcessStartOptions ,
833+ ) -> InProcessClientHandle {
834+ let ( codex_home, args) = build_test_start_args ( session_source, channel_capacity) . await ;
835+ let mut client = start_with_options ( args, options)
836+ . await
837+ . expect ( "in-process runtime should start" ) ;
838+ client. _test_codex_home = Some ( codex_home) ;
839+ client
840+ }
841+
842+ async fn start_test_client_with_capacity (
843+ session_source : SessionSource ,
844+ channel_capacity : usize ,
845+ ) -> InProcessClientHandle {
846+ let ( codex_home, args) = build_test_start_args ( session_source, channel_capacity) . await ;
797847 let mut client = start ( args) . await . expect ( "in-process runtime should start" ) ;
798848 client. _test_codex_home = Some ( codex_home) ;
799849 client
@@ -852,6 +902,45 @@ mod tests {
852902 }
853903 }
854904
905+ #[ tokio:: test]
906+ async fn in_process_start_uses_injected_thread_store ( ) {
907+ let thread_store = Arc :: new ( InMemoryThreadStore :: default ( ) ) ;
908+ let client = start_test_client_with_capacity_and_options (
909+ SessionSource :: Cli ,
910+ DEFAULT_IN_PROCESS_CHANNEL_CAPACITY ,
911+ InProcessStartOptions :: default ( ) . with_thread_store ( thread_store. clone ( ) ) ,
912+ )
913+ . await ;
914+
915+ client
916+ . request ( ClientRequest :: ThreadList {
917+ request_id : RequestId :: Integer ( 3 ) ,
918+ params : ThreadListParams {
919+ cursor : None ,
920+ limit : Some ( 1 ) ,
921+ sort_key : None ,
922+ sort_direction : None ,
923+ model_providers : Some ( Vec :: new ( ) ) ,
924+ source_kinds : None ,
925+ archived : None ,
926+ cwd : None ,
927+ use_state_db_only : false ,
928+ search_term : None ,
929+ parent_thread_id : None ,
930+ ancestor_thread_id : None ,
931+ } ,
932+ } )
933+ . await
934+ . expect ( "request transport should work" )
935+ . expect ( "thread/list should succeed" ) ;
936+
937+ assert_eq ! ( thread_store. calls( ) . await . list_threads, 1 ) ;
938+ client
939+ . shutdown ( )
940+ . await
941+ . expect ( "in-process runtime should shutdown cleanly" ) ;
942+ }
943+
855944 #[ tokio:: test]
856945 async fn in_process_start_clamps_zero_channel_capacity ( ) {
857946 let client =
0 commit comments