diff --git a/src/main/claude/claude-structured-session-acquisition.ts b/src/main/claude/claude-structured-session-acquisition.ts index 8b23c9a8d70..3da84c21129 100644 --- a/src/main/claude/claude-structured-session-acquisition.ts +++ b/src/main/claude/claude-structured-session-acquisition.ts @@ -143,6 +143,7 @@ export async function acquireClaudeSession({ ) } acquisitions.assertCurrent(sessionId, attempt) + const priorSession = sessions.get(sessionId) if (!(await closeClaudePublishedSessionForDeps(sessions, sessionId, deps))) { throw new AgentSessionAcquisitionExitUnprovenError( new Error(`claude session ${sessionId} could not be stopped`) @@ -151,8 +152,19 @@ export async function acquireClaudeSession({ // Any earlier session is closed or already gone: its exit says nothing about this start. exits.delete(sessionId) acquisitions.assertCurrent(sessionId, attempt) + // Closing persists the prior connection's final leaf, so launch validates that durable head. + const launchIdentity = priorSession + ? { + ...input.identity, + providerHandle: { + kind: 'claude' as const, + sessionId: priorSession.providerSessionId, + leafUuid: priorSession.leafUuid + } + } + : input.identity const launch = await deps - .resolveLaunch({ identity: input.identity }) + .resolveLaunch({ identity: launchIdentity }) .catch((error: unknown) => { throw error instanceof AgentSessionPreSpawnError ? error diff --git a/src/main/runtime/claude-structured-session-integration.test.ts b/src/main/runtime/claude-structured-session-integration.test.ts index 2a804ed822a..f4366caf5d4 100644 --- a/src/main/runtime/claude-structured-session-integration.test.ts +++ b/src/main/runtime/claude-structured-session-integration.test.ts @@ -563,14 +563,27 @@ describe('a structured Claude session over agentSession.*', () => { ).resolves.toMatchObject({ turnId: 'user-1', cancelled: true }) expect(claude.live().calls.at(-1)).toMatchObject({ subtype: 'interrupt' }) + const host = getStructuredAgentSessionHost() as unknown as { + deps: { + store: { + getRecord: (sessionId: string) => { + providerHandleChain: { handle: { provider: string; leafUuid?: string | null } }[] + } + } + } + } + expect(host.deps.store.getRecord(SESSION).providerHandleChain.at(-1)?.handle).toMatchObject({ + provider: 'claude', + leafUuid: null + }) const old = claude.live() const resumed = await ok<{ fence: number }>('agentSession.ensure', ensureParams(created.fence)) expect(resumed.fence).toBe(created.fence + 1) expect(old.closed).toBe(true) - expect(claude.live().launch.options).toMatchObject({ resume: PROVIDER_SESSION }) - const host = getStructuredAgentSessionHost() as unknown as { - deps: { store: { getRecord: (sessionId: string) => { providerHandleChain: unknown[] } } } - } + expect(claude.live().launch.options).toMatchObject({ + resume: PROVIDER_SESSION, + resumeSessionAt: 'assistant-leaf' + }) expect(host.deps.store.getRecord(SESSION).providerHandleChain.at(-1)).toMatchObject({ handle: { provider: 'claude',