@@ -39,6 +39,7 @@ vi.mock("openclaw/plugin-sdk/agent-runtime", () => ({
3939let listCodexAppServerModels : typeof import ( "./models.js" ) . listCodexAppServerModels ;
4040let clearSharedCodexAppServerClient : typeof import ( "./shared-client.js" ) . clearSharedCodexAppServerClient ;
4141let clearSharedCodexAppServerClientIfCurrent : typeof import ( "./shared-client.js" ) . clearSharedCodexAppServerClientIfCurrent ;
42+ let clearSharedCodexAppServerClientIfCurrentAndWait : typeof import ( "./shared-client.js" ) . clearSharedCodexAppServerClientIfCurrentAndWait ;
4243let createIsolatedCodexAppServerClient : typeof import ( "./shared-client.js" ) . createIsolatedCodexAppServerClient ;
4344let getSharedCodexAppServerClient : typeof import ( "./shared-client.js" ) . getSharedCodexAppServerClient ;
4445let resetSharedCodexAppServerClientForTests : typeof import ( "./shared-client.js" ) . resetSharedCodexAppServerClientForTests ;
@@ -111,6 +112,7 @@ describe("shared Codex app-server client", () => {
111112 ( {
112113 clearSharedCodexAppServerClient,
113114 clearSharedCodexAppServerClientIfCurrent,
115+ clearSharedCodexAppServerClientIfCurrentAndWait,
114116 createIsolatedCodexAppServerClient,
115117 getSharedCodexAppServerClient,
116118 resetSharedCodexAppServerClientForTests,
@@ -476,6 +478,44 @@ describe("shared Codex app-server client", () => {
476478 expect ( second . process . stdin . destroyed ) . toBe ( true ) ;
477479 } ) ;
478480
481+ it ( "waits only for the shared client that is still current" , async ( ) => {
482+ const first = createClientHarness ( ) ;
483+ const second = createClientHarness ( ) ;
484+ vi . spyOn ( CodexAppServerClient , "start" )
485+ . mockReturnValueOnce ( first . client )
486+ . mockReturnValueOnce ( second . client ) ;
487+ const firstCloseAndWait = vi . spyOn ( first . client , "closeAndWait" ) ;
488+ const secondCloseAndWait = vi . spyOn ( second . client , "closeAndWait" ) ;
489+
490+ const firstList = listCodexAppServerModels ( {
491+ timeoutMs : 1000 ,
492+ agentDir : "/tmp/openclaw-agent-one" ,
493+ } ) ;
494+ await sendInitializeResult ( first , "openclaw/0.125.0 (macOS; test)" ) ;
495+ await sendEmptyModelList ( first ) ;
496+ await expect ( firstList ) . resolves . toEqual ( { models : [ ] } ) ;
497+
498+ const secondList = listCodexAppServerModels ( {
499+ timeoutMs : 1000 ,
500+ agentDir : "/tmp/openclaw-agent-two" ,
501+ } ) ;
502+ await sendInitializeResult ( second , "openclaw/0.125.0 (macOS; test)" ) ;
503+ await sendEmptyModelList ( second ) ;
504+ await expect ( secondList ) . resolves . toEqual ( { models : [ ] } ) ;
505+
506+ await expect (
507+ clearSharedCodexAppServerClientIfCurrentAndWait ( first . client , {
508+ exitTimeoutMs : 25 ,
509+ forceKillDelayMs : 5 ,
510+ } ) ,
511+ ) . resolves . toBe ( true ) ;
512+
513+ expect ( firstCloseAndWait ) . toHaveBeenCalledTimes ( 1 ) ;
514+ expect ( secondCloseAndWait ) . not . toHaveBeenCalled ( ) ;
515+ expect ( first . process . stdin . destroyed ) . toBe ( true ) ;
516+ expect ( second . process . stdin . destroyed ) . toBe ( false ) ;
517+ } ) ;
518+
479519 it ( "uses a fresh websocket Authorization header after shared-client token rotation" , async ( ) => {
480520 const server = new WebSocketServer ( { host : "127.0.0.1" , port : 0 } ) ;
481521 const authHeaders : Array < string | undefined > = [ ] ;
0 commit comments