@@ -46,7 +46,12 @@ function makeStubRpc(responses: Record<string, unknown>) {
4646 message : `Invalid request: missing field \`${ missing } \`` ,
4747 } ;
4848 }
49- return ( responses [ method ] ?? { } ) as T ;
49+ const response = responses [ method ] ;
50+ return (
51+ typeof response === "function"
52+ ? await response ( params )
53+ : ( response ?? { } )
54+ ) as T ;
5055 } ,
5156 notify ( ) { } ,
5257 async close ( ) { } ,
@@ -494,6 +499,52 @@ describe("CodexAppServerAgent", () => {
494499 } ) ;
495500 } ) ;
496501
502+ it ( "retries queued goal cancellation after an interrupt failure" , async ( ) => {
503+ let interruptAttempts = 0 ;
504+ const stub = makeStubRpc ( {
505+ "thread/start" : { thread : { id : "thr_1" } } ,
506+ "thread/goal/set" : {
507+ goal : { objective : "Ship the fix" , status : "paused" } ,
508+ } ,
509+ "turn/interrupt" : ( ) => {
510+ interruptAttempts ++ ;
511+ if ( interruptAttempts === 1 ) {
512+ throw new Error ( "interrupt failed" ) ;
513+ }
514+ return { } ;
515+ } ,
516+ } ) ;
517+ const { client } = makeFakeClient ( ) ;
518+ const agent = new CodexAppServerAgent ( client , {
519+ processOptions : { binaryPath : "/bundle/codex" } ,
520+ rpcFactory : stub . factory ,
521+ } ) ;
522+ await agent . newSession ( { cwd : "/repo" } as unknown as NewSessionRequest ) ;
523+
524+ await agent . prompt ( {
525+ sessionId : "thr_1" ,
526+ prompt : [ { type : "text" , text : "/goal pause" } ] ,
527+ } as unknown as PromptRequest ) ;
528+ stub . emit ( "turn/started" , { turn : { id : "goal_tick_1" } } ) ;
529+ await Promise . resolve ( ) ;
530+ await Promise . resolve ( ) ;
531+ stub . emit ( "turn/started" , { turn : { id : "goal_tick_2" } } ) ;
532+ await Promise . resolve ( ) ;
533+
534+ expect (
535+ stub . requests . filter ( ( request ) => request . method === "turn/interrupt" ) ,
536+ ) . toEqual ( [
537+ {
538+ method : "turn/interrupt" ,
539+ params : { threadId : "thr_1" , turnId : "goal_tick_1" } ,
540+ } ,
541+ {
542+ method : "turn/interrupt" ,
543+ params : { threadId : "thr_1" , turnId : "goal_tick_2" } ,
544+ } ,
545+ ] ) ;
546+ } ) ;
547+
497548 it ( "includes buffered command output when completion omits aggregatedOutput" , async ( ) => {
498549 const stub = makeStubRpc ( {
499550 initialize : { } ,
0 commit comments