@@ -1840,34 +1840,49 @@ it.effect.each(["Stop", "Force stop"] as const)(
18401840) ;
18411841
18421842it . effect . each ( [
1843- { followUp : "started its turn" , startedBeforeRelease : true , switchesInstance : false } ,
1844- { followUp : "was promoted" , startedBeforeRelease : false , switchesInstance : false } ,
1843+ { followUp : "started its turn" , startedBeforeRelease : true } ,
1844+ { followUp : "was promoted" , startedBeforeRelease : false } ,
18451845 { followUp : "switched provider instance" , startedBeforeRelease : true , switchesInstance : true } ,
1846+ { followUp : "has fresh context" , startedBeforeRelease : true , freshContext : true } ,
18461847] ) (
18471848 "Force stop keeps only the session of a queued follow-up that $followUp" ,
1848- ( { startedBeforeRelease, switchesInstance } ) =>
1849+ ( { startedBeforeRelease, switchesInstance = false , freshContext = false } ) =>
18491850 Effect . scoped (
18501851 Effect . gen ( function * ( ) {
18511852 const cwd = yield * checkpointWorkspace ( "thread-stop-force-stop-follow-up" ) ;
18521853 const driver = adapter . driver ;
18531854 const otherInstanceId = ProviderInstanceId . make ( "codex-other" ) ;
1854- const eventQueues = new Map ( [
1855- [ instanceId , yield * Queue . unbounded < ProviderAdapter . ProviderAdapterV2Event > ( ) ] ,
1856- [ otherInstanceId , yield * Queue . unbounded < ProviderAdapter . ProviderAdapterV2Event > ( ) ] ,
1857- ] ) ;
1855+ const eventQueues = new Map <
1856+ ProviderSessionId ,
1857+ Queue . Queue < ProviderAdapter . ProviderAdapterV2Event >
1858+ > ( ) ;
18581859 const started : Array < ProviderAdapter . ProviderAdapterV2TurnInput > = [ ] ;
18591860 const interrupted : Array < ProviderAdapter . ProviderAdapterV2InterruptInput > = [ ] ;
18601861 let opened = 0 ;
1862+ // Fresh context on a one-thread-per-session provider puts the follow-up on its own session.
1863+ // The session itself reports shared threads so fresh-context cleanup leaves the stopped
1864+ // run's session to Force stop, as pending background work would.
1865+ const adapterCapabilities = freshContext
1866+ ? {
1867+ ...CodexProviderCapabilitiesV2 ,
1868+ sessions : {
1869+ ...CodexProviderCapabilitiesV2 . sessions ,
1870+ supportsMultipleProviderThreadsPerSession : false ,
1871+ } ,
1872+ }
1873+ : CodexProviderCapabilitiesV2 ;
18611874 const liveAdapter = (
18621875 instanceId : ProviderInstanceId ,
18631876 ) : ProviderAdapter . ProviderAdapterV2 [ "Service" ] => ( {
18641877 ...adapter ,
18651878 instanceId,
1879+ getCapabilities : ( ) => Effect . succeed ( adapterCapabilities ) ,
18661880 openSession : ( input ) =>
18671881 Effect . gen ( function * ( ) {
18681882 opened += 1 ;
18691883 const now = yield * DateTime . now ;
1870- const events = eventQueues . get ( instanceId ) ! ;
1884+ const events = yield * Queue . unbounded < ProviderAdapter . ProviderAdapterV2Event > ( ) ;
1885+ eventQueues . set ( input . providerSessionId , events ) ;
18711886 return {
18721887 instanceId,
18731888 driver,
@@ -1887,7 +1902,9 @@ it.effect.each([
18871902 events : Stream . fromQueue ( events ) ,
18881903 ensureThread : ( { threadId } ) =>
18891904 Effect . succeed ( {
1890- id : ProviderThreadId . make ( `provider-thread:${ instanceId } :${ threadId } ` ) ,
1905+ id : ProviderThreadId . make (
1906+ `provider-thread:${ input . providerSessionId } :${ threadId } ` ,
1907+ ) ,
18911908 driver,
18921909 providerInstanceId : instanceId ,
18931910 providerSessionId : input . providerSessionId ,
@@ -2017,6 +2034,7 @@ it.effect.each([
20172034 ...( switchesInstance
20182035 ? { modelSelection : { ...modelSelection , instanceId : otherInstanceId } }
20192036 : { } ) ,
2037+ ...( freshContext ? { freshContext } : { } ) ,
20202038 createdBy : "user" ,
20212039 creationSource : "web" ,
20222040 } ) ;
@@ -2031,15 +2049,15 @@ it.effect.each([
20312049 ) ;
20322050 assert . equal (
20332051 opened ,
2034- switchesInstance ? 2 : 1 ,
2052+ switchesInstance || freshContext ? 2 : 1 ,
20352053 "Force stop tore down the session the follow-up was promoted onto" ,
20362054 ) ;
20372055 const [ stopped , followUp ] = [ started [ 0 ] ! , started [ 1 ] ! ] ;
20382056 const providerSessionId = followUp . providerThread . providerSessionId ! ;
20392057 assert . equal (
20402058 yield * sessions . isLive ( stopped . providerThread . providerSessionId ! ) ,
2041- ! switchesInstance ,
2042- "Force stop kept the stopped run's session for a follow-up on another instance " ,
2059+ ! switchesInstance && ! freshContext ,
2060+ "Force stop kept the stopped run's session for a follow-up on another session " ,
20432061 ) ;
20442062 assert . isFalse (
20452063 interrupted . some ( ( interrupt ) =>
@@ -2058,7 +2076,7 @@ it.effect.each([
20582076 ) ;
20592077
20602078 // The first run's provider never confirms the stop; the follow-up's turn ends normally.
2061- yield * Queue . offer ( eventQueues . get ( followUp . providerThread . providerInstanceId ) ! , {
2079+ yield * Queue . offer ( eventQueues . get ( providerSessionId ) ! , {
20622080 type : "turn.terminal" ,
20632081 driver,
20642082 providerThreadId : followUp . providerThread . id ,
0 commit comments