@@ -3276,6 +3276,85 @@ describe("runCodexAppServerAttempt", () => {
32763276 expect ( result . promptError ) . toBeNull ( ) ;
32773277 } ) ;
32783278
3279+ it ( "keeps turn request activity active until elicitation handling resolves" , async ( ) => {
3280+ const harness = createStartedThreadHarness ( ) ;
3281+ const bridgedResponse = {
3282+ action : "accept" ,
3283+ content : null ,
3284+ _meta : null ,
3285+ } as const ;
3286+ let resolveBridge ! : ( value : typeof bridgedResponse ) => void ;
3287+ const bridgePromise = new Promise < typeof bridgedResponse > ( ( resolve ) => {
3288+ resolveBridge = resolve ;
3289+ } ) ;
3290+ vi . spyOn ( elicitationBridge , "handleCodexAppServerElicitationRequest" ) . mockImplementation (
3291+ async ( ) => await bridgePromise ,
3292+ ) ;
3293+ const params = createParams (
3294+ path . join ( tempDir , "session.jsonl" ) ,
3295+ path . join ( tempDir , "workspace" ) ,
3296+ ) ;
3297+ params . timeoutMs = 500 ;
3298+ const onRunProgress = vi . fn ( ) ;
3299+ params . onRunProgress = onRunProgress ;
3300+
3301+ const run = runCodexAppServerAttempt ( params , {
3302+ turnCompletionIdleTimeoutMs : 1_000 ,
3303+ turnAssistantCompletionIdleTimeoutMs : 1_000 ,
3304+ turnTerminalIdleTimeoutMs : 1_000 ,
3305+ } ) ;
3306+ await harness . waitForMethod ( "turn/start" ) ;
3307+
3308+ const response = harness . handleServerRequest ( {
3309+ id : "request-pending-elicitation" ,
3310+ method : "mcpServer/elicitation/request" ,
3311+ params : {
3312+ threadId : "thread-1" ,
3313+ turnId : "turn-1" ,
3314+ mode : "form" ,
3315+ message : "Approve?" ,
3316+ requestedSchema : { type : "object" , properties : { } } ,
3317+ serverName : "server-1" ,
3318+ _meta : null ,
3319+ } ,
3320+ } ) ;
3321+ await vi . waitFor (
3322+ ( ) =>
3323+ expect ( onRunProgress ) . toHaveBeenCalledWith (
3324+ expect . objectContaining ( {
3325+ reason : "request:mcpServer/elicitation/request:start" ,
3326+ } ) ,
3327+ ) ,
3328+ fastWait ,
3329+ ) ;
3330+ await new Promise ( ( resolve ) => setTimeout ( resolve , 60 ) ) ;
3331+ expect (
3332+ onRunProgress . mock . calls . some (
3333+ ( [ event ] ) =>
3334+ ( event as { reason ?: string } ) . reason ===
3335+ "request:mcpServer/elicitation/request:response" ,
3336+ ) ,
3337+ ) . toBe ( false ) ;
3338+
3339+ resolveBridge ( bridgedResponse ) ;
3340+ await expect ( response ) . resolves . toEqual ( bridgedResponse ) ;
3341+ await vi . waitFor (
3342+ ( ) =>
3343+ expect ( onRunProgress ) . toHaveBeenCalledWith (
3344+ expect . objectContaining ( {
3345+ reason : "request:mcpServer/elicitation/request:response" ,
3346+ } ) ,
3347+ ) ,
3348+ fastWait ,
3349+ ) ;
3350+ await harness . completeTurn ( { threadId : "thread-1" , turnId : "turn-1" } ) ;
3351+
3352+ const result = await run ;
3353+ expect ( result . aborted ) . toBe ( false ) ;
3354+ expect ( result . timedOut ) . toBe ( false ) ;
3355+ expect ( result . promptError ) . toBeNull ( ) ;
3356+ } ) ;
3357+
32793358 it ( "counts pending user input requests as turn attempt progress" , async ( ) => {
32803359 const harness = createStartedThreadHarness ( ) ;
32813360 const params = createParams (
0 commit comments