@@ -7937,6 +7937,119 @@ describe("CodexAdapterV2 post-settle continuation", () => {
79377937 } ) . pipe ( Effect . scoped , Effect . provide ( Layer . merge ( IdAllocator . layer , NodeServices . layer ) ) ) ,
79387938 ) ;
79397939
7940+ it . effect ( "stops an earlier goal turn's subagent when the run settles during the pause" , ( ) =>
7941+ Effect . gen ( function * ( ) {
7942+ const prompt = "Delegate the audit" ;
7943+ const childThreadId = "goal-race-child-thread" ;
7944+ const childTurnId = "goal-race-child-turn" ;
7945+ const transcript = makeCodexReplayTranscript ( {
7946+ scenario : "goal-stop-run-settles-during-pause" ,
7947+ entries : [
7948+ ...codexReplayPreamble ( { nativeThreadId, nativeTurnId : "goal-turn-a" , prompt } ) ,
7949+ notification ( "subagent started" , "item/completed" , {
7950+ item : {
7951+ type : "subAgentActivity" ,
7952+ id : "goal-race-subagent-call" ,
7953+ kind : "started" ,
7954+ agentThreadId : childThreadId ,
7955+ agentPath : "/root/audit" ,
7956+ } ,
7957+ threadId : nativeThreadId ,
7958+ turnId : "goal-turn-a" ,
7959+ completedAtMs : 1782622441000 ,
7960+ } ) ,
7961+ notification ( "child turn" , "turn/started" , {
7962+ threadId : childThreadId ,
7963+ turn : makeCodexReplayTurn ( { id : childTurnId , status : "inProgress" } ) ,
7964+ } ) ,
7965+ notification ( "goal active" , "thread/goal/updated" , {
7966+ threadId : nativeThreadId ,
7967+ turnId : "goal-turn-a" ,
7968+ goal : codexGoal ( "active" , 10 ) ,
7969+ } ) ,
7970+ notification ( "first turn done" , "turn/completed" , {
7971+ threadId : nativeThreadId ,
7972+ turn : makeCodexReplayTurn ( { id : "goal-turn-a" , status : "completed" } ) ,
7973+ } ) ,
7974+ notification ( "continuation" , "turn/started" , {
7975+ threadId : nativeThreadId ,
7976+ turn : makeCodexReplayTurn ( { id : "goal-turn-b" , status : "inProgress" } ) ,
7977+ } ) ,
7978+ notification ( "goal accounted" , "thread/goal/updated" , {
7979+ threadId : nativeThreadId ,
7980+ turnId : "goal-turn-b" ,
7981+ goal : codexGoal ( "active" , 11 ) ,
7982+ } ) ,
7983+ {
7984+ type : "expect_outbound" ,
7985+ label : "thread/goal/set" ,
7986+ frame : {
7987+ id : 4 ,
7988+ method : "thread/goal/set" ,
7989+ params : { threadId : nativeThreadId , status : "paused" } ,
7990+ } ,
7991+ } ,
7992+ notification ( "goal paused" , "thread/goal/updated" , {
7993+ threadId : nativeThreadId ,
7994+ turnId : "goal-turn-b" ,
7995+ goal : codexGoal ( "paused" , 11 ) ,
7996+ } ) ,
7997+ notification ( "continuation done" , "turn/completed" , {
7998+ threadId : nativeThreadId ,
7999+ turn : makeCodexReplayTurn ( { id : "goal-turn-b" , status : "completed" } ) ,
8000+ } ) ,
8001+ {
8002+ type : "emit_inbound" ,
8003+ label : "thread/goal/set" ,
8004+ frame : { id : 4 , result : { goal : codexGoal ( "paused" , 11 ) } } ,
8005+ } ,
8006+ ...request ( 5 , "turn/interrupt" , { threadId : childThreadId , turnId : childTurnId } , { } ) ,
8007+ notification ( "child interrupted" , "turn/completed" , {
8008+ threadId : childThreadId ,
8009+ turn : makeCodexReplayTurn ( { id : childTurnId , status : "interrupted" } ) ,
8010+ } ) ,
8011+ ] ,
8012+ } ) ;
8013+ const harness = yield * makeCodexReplayHarness ( transcript ) ;
8014+ yield * harness . runtime . startTurn (
8015+ makeCodexTestTurnInput ( {
8016+ threadId : harness . threadId ,
8017+ providerThread : harness . providerThread ,
8018+ now : yield * DateTime . now ,
8019+ attemptId : RunAttemptId . make ( "goal-settles-during-pause-attempt" ) ,
8020+ text : prompt ,
8021+ } ) ,
8022+ ) ;
8023+ yield * awaitUntil (
8024+ ( ) =>
8025+ harness . events . some (
8026+ ( event ) =>
8027+ event . type === "provider_thread.updated" &&
8028+ event . providerThread . goal ?. tokensUsed === 11 ,
8029+ ) ,
8030+ "the continuation to be adopted" ,
8031+ ) ;
8032+ yield * harness . runtime . interruptTurn ( {
8033+ providerThread : harness . providerThread ,
8034+ providerTurnId : ( yield * IdAllocator . IdAllocatorV2 ) . derive . providerTurn ( {
8035+ driver : CodexAdapterV2 . CODEX_DRIVER_KIND ,
8036+ nativeTurnId : "goal-turn-b" ,
8037+ } ) ,
8038+ requestRuntimeRestart : true ,
8039+ } ) ;
8040+ yield * awaitUntil (
8041+ ( ) =>
8042+ harness . events . some (
8043+ ( event ) =>
8044+ event . type === "provider_turn.updated" &&
8045+ event . providerTurn . nativeTurnRef ?. nativeId === childTurnId &&
8046+ event . providerTurn . status === "interrupted" ,
8047+ ) ,
8048+ "the subagent turn to be interrupted" ,
8049+ ) ;
8050+ } ) . pipe ( Effect . scoped , Effect . provide ( Layer . merge ( IdAllocator . layer , NodeServices . layer ) ) ) ,
8051+ ) ;
8052+
79408053 it . effect ( "pauses an active goal before Stop interrupts its turn" , ( ) =>
79418054 Effect . gen ( function * ( ) {
79428055 const prompt = "Keep going" ;
0 commit comments