@@ -3822,6 +3822,85 @@ describe("runCodexAppServerAttempt", () => {
38223822 expect ( result . promptError ) . toBeNull ( ) ;
38233823 } ) ;
38243824
3825+ it ( "keeps turn request activity active until elicitation handling resolves" , async ( ) => {
3826+ const harness = createStartedThreadHarness ( ) ;
3827+ const bridgedResponse = {
3828+ action : "accept" ,
3829+ content : null ,
3830+ _meta : null ,
3831+ } as const ;
3832+ let resolveBridge ! : ( value : typeof bridgedResponse ) => void ;
3833+ const bridgePromise = new Promise < typeof bridgedResponse > ( ( resolve ) => {
3834+ resolveBridge = resolve ;
3835+ } ) ;
3836+ vi . spyOn ( elicitationBridge , "handleCodexAppServerElicitationRequest" ) . mockImplementation (
3837+ async ( ) => await bridgePromise ,
3838+ ) ;
3839+ const params = createParams (
3840+ path . join ( tempDir , "session.jsonl" ) ,
3841+ path . join ( tempDir , "workspace" ) ,
3842+ ) ;
3843+ params . timeoutMs = 500 ;
3844+ const onRunProgress = vi . fn ( ) ;
3845+ params . onRunProgress = onRunProgress ;
3846+
3847+ const run = runCodexAppServerAttempt ( params , {
3848+ turnCompletionIdleTimeoutMs : 1_000 ,
3849+ turnAssistantCompletionIdleTimeoutMs : 1_000 ,
3850+ turnTerminalIdleTimeoutMs : 1_000 ,
3851+ } ) ;
3852+ await harness . waitForMethod ( "turn/start" ) ;
3853+
3854+ const response = harness . handleServerRequest ( {
3855+ id : "request-pending-elicitation" ,
3856+ method : "mcpServer/elicitation/request" ,
3857+ params : {
3858+ threadId : "thread-1" ,
3859+ turnId : "turn-1" ,
3860+ mode : "form" ,
3861+ message : "Approve?" ,
3862+ requestedSchema : { type : "object" , properties : { } } ,
3863+ serverName : "server-1" ,
3864+ _meta : null ,
3865+ } ,
3866+ } ) ;
3867+ await vi . waitFor (
3868+ ( ) =>
3869+ expect ( onRunProgress ) . toHaveBeenCalledWith (
3870+ expect . objectContaining ( {
3871+ reason : "request:mcpServer/elicitation/request:start" ,
3872+ } ) ,
3873+ ) ,
3874+ fastWait ,
3875+ ) ;
3876+ await new Promise ( ( resolve ) => setTimeout ( resolve , 60 ) ) ;
3877+ expect (
3878+ onRunProgress . mock . calls . some (
3879+ ( [ event ] ) =>
3880+ ( event as { reason ?: string } ) . reason ===
3881+ "request:mcpServer/elicitation/request:response" ,
3882+ ) ,
3883+ ) . toBe ( false ) ;
3884+
3885+ resolveBridge ( bridgedResponse ) ;
3886+ await expect ( response ) . resolves . toEqual ( bridgedResponse ) ;
3887+ await vi . waitFor (
3888+ ( ) =>
3889+ expect ( onRunProgress ) . toHaveBeenCalledWith (
3890+ expect . objectContaining ( {
3891+ reason : "request:mcpServer/elicitation/request:response" ,
3892+ } ) ,
3893+ ) ,
3894+ fastWait ,
3895+ ) ;
3896+ await harness . completeTurn ( { threadId : "thread-1" , turnId : "turn-1" } ) ;
3897+
3898+ const result = await run ;
3899+ expect ( result . aborted ) . toBe ( false ) ;
3900+ expect ( result . timedOut ) . toBe ( false ) ;
3901+ expect ( result . promptError ) . toBeNull ( ) ;
3902+ } ) ;
3903+
38253904 it ( "counts pending user input requests as turn attempt progress" , async ( ) => {
38263905 const harness = createStartedThreadHarness ( ) ;
38273906 const params = createParams (
0 commit comments