@@ -29,6 +29,7 @@ import {
2929 truncateSessionLabel ,
3030} from "../../session/session-label.js" ;
3131import { clearActiveDisposeHost } from "../../session/active-host.js" ;
32+ import { syncRunStateHandle } from "../../session/active-run.js" ;
3233import { getValidCodexToken } from "../../auth/codex/session.js" ;
3334import { getValidXaiToken } from "../../auth/xai/session.js" ;
3435import { suppressProviderFailurePresentation } from "../provider/failure-attempt.js" ;
@@ -185,12 +186,16 @@ function createRunPersistence(state: RunnerState, services: RunnerServices) {
185186 const model = `${ state . liveSource . id } :${ state . liveSource . model } ` ;
186187 // Kept in step with every persisted snapshot so the crash handler's copy
187188 // (activeRunHandle, read by index.ts) never lags what's actually on disk.
188- services . activeRunHandle . task = task ;
189- services . activeRunHandle . startedAt = state . startedAt ;
190- services . activeRunHandle . model = model ;
189+ const turnsUsed = services . runSink . getTurnCount ( ) ;
190+ syncRunStateHandle ( services . activeRunHandle , {
191+ turnsUsed,
192+ task,
193+ startedAt : state . startedAt ,
194+ model,
195+ } ) ;
191196 const persisted : RunState = {
192197 status,
193- turnsUsed : services . runSink . getTurnCount ( ) ,
198+ turnsUsed,
194199 task,
195200 startedAt : state . startedAt ,
196201 model,
@@ -586,11 +591,25 @@ export async function createRunLifecycle(
586591 "session-rotation" ,
587592 ) ;
588593 state . sessionId = generateSessionId ( ) ;
589- // Repointed, not cleared: the process lives on, so the crash handler
590- // must keep finding this handle and close out the *new* session.
591- services . activeRunHandle . sessionId = state . sessionId ;
592594 state . startedAt = Date . now ( ) ;
593595 state . runTaskTitle = state . config . task ;
596+ const rotatedBundle = services . buildSessionSources ( ) ;
597+ // Repointed, not cleared: the process lives on, so the crash handler
598+ // must keep finding this handle and close out the *new* session. The
599+ // fields it copies reseed with the repoint — a crash inside
600+ // initSessionDir/buildAgent below would otherwise stamp the outgoing
601+ // session's turnsUsed (and task, startedAt, model) onto a session
602+ // that has run zero turns.
603+ services . activeRunHandle . sessionId = state . sessionId ;
604+ syncRunStateHandle ( services . activeRunHandle , {
605+ turnsUsed : 0 ,
606+ task :
607+ state . runTaskTitle . trim ( ) . length > 0
608+ ? state . runTaskTitle . trim ( )
609+ : "(conversation)" ,
610+ startedAt : state . startedAt ,
611+ model : `${ rotatedBundle . selected . id } :${ rotatedBundle . selected . model } ` ,
612+ } ) ;
594613 services . emitter . emit (
595614 "session.title" ,
596615 state . runTaskTitle . trim ( ) . length > 0
@@ -599,7 +618,6 @@ export async function createRunLifecycle(
599618 ) ;
600619 state . workdir = sessionContextDir ( state . config . cwd , state . sessionId ) ;
601620 await initSessionDir ( state . config . cwd , state . sessionId ) ;
602- const rotatedBundle = services . buildSessionSources ( ) ;
603621 state . liveSources = rotatedBundle . sources ;
604622 state . liveDefaultSource = rotatedBundle . defaultSource ;
605623 state . liveSource = rotatedBundle . selected ;
0 commit comments