@@ -39,6 +39,7 @@ import type {
3939 SessionConfigOption ,
4040} from '@agentclientprotocol/sdk'
4141import { randomUUID , type UUID } from 'node:crypto'
42+ import { dirname } from 'node:path'
4243import type { Message } from '../../types/message.js'
4344import { deserializeMessages } from '../../utils/conversationRecovery.js'
4445import {
@@ -53,7 +54,11 @@ import { getEmptyToolPermissionContext } from '../../Tool.js'
5354import type { PermissionMode } from '../../types/permissions.js'
5455import type { Command } from '../../types/command.js'
5556import { getCommands } from '../../commands.js'
56- import { setOriginalCwd , switchSession } from '../../bootstrap/state.js'
57+ import {
58+ setOriginalCwd ,
59+ switchSession ,
60+ getSessionProjectDir ,
61+ } from '../../bootstrap/state.js'
5762import type { SessionId } from '../../types/ids.js'
5863import { enableConfigs } from '../../utils/config.js'
5964import { FileStateCache } from '../../utils/fileStateCache.js'
@@ -72,6 +77,7 @@ import {
7277} from './utils.js'
7378import { promptToQueryInput } from './promptConversion.js'
7479import { listSessionsImpl } from '../../utils/listSessionsImpl.js'
80+ import { resolveSessionFilePath } from '../../utils/sessionStoragePortable.js'
7581import { getMainLoopModel } from '../../utils/model/model.js'
7682import { getModelOptions } from '../../utils/model/modelOptions.js'
7783import { getSettings_DEPRECATED } from '../../utils/settings/settings.js'
@@ -474,7 +480,10 @@ export class AcpAgent implements Agent {
474480
475481 // Align the global session state so that transcript persistence,
476482 // analytics, and cost tracking use the ACP session ID.
477- switchSession ( sessionId as SessionId )
483+ // Preserve the projectDir set by getOrCreateSession so that
484+ // getSessionProjectDir() continues to resolve correctly.
485+ const currentProjectDir = getSessionProjectDir ( )
486+ switchSession ( sessionId as SessionId , currentProjectDir )
478487
479488 // Set CWD for the session
480489 setOriginalCwd ( cwd )
@@ -680,8 +689,18 @@ export class AcpAgent implements Agent {
680689 | undefined ,
681690 } )
682691 if ( fingerprint === existingSession . sessionFingerprint ) {
683- // Align global state so subsequent operations use the correct session
684- switchSession ( params . sessionId as SessionId )
692+ const resolved = await resolveSessionFilePath (
693+ params . sessionId ,
694+ params . cwd ,
695+ )
696+ switchSession (
697+ params . sessionId as SessionId ,
698+ resolved ? dirname ( resolved . filePath ) : null ,
699+ )
700+ setOriginalCwd ( params . cwd )
701+
702+ await this . replaySessionHistory ( params )
703+
685704 return {
686705 sessionId : params . sessionId ,
687706 modes : existingSession . modes ,
@@ -690,20 +709,20 @@ export class AcpAgent implements Agent {
690709 }
691710 }
692711
693- // Session-defining params changed — tear down and recreate
694712 await this . teardownSession ( params . sessionId )
695713 }
696714
697- // Align global state BEFORE sessionIdExists() check — the lookup uses
698- // getSessionId() internally when resolving project-scoped paths.
699- switchSession ( params . sessionId as SessionId )
700-
701- // Set CWD early so session file lookup can find the right project directory
715+ // Locate the session file by sessionId across all project directories.
716+ // params.cwd may not match the project directory where the session was
717+ // originally created (e.g. client sends a subdirectory path), so we
718+ // search by sessionId first and fall back to cwd-based lookup.
719+ const resolved = await resolveSessionFilePath ( params . sessionId , params . cwd )
720+ const projectDir = resolved ? dirname ( resolved . filePath ) : null
721+ switchSession ( params . sessionId as SessionId , projectDir )
702722 setOriginalCwd ( params . cwd )
703723
704- // Try to load session history for resume/load
705724 let initialMessages : Message [ ] | undefined
706- if ( sessionIdExists ( params . sessionId ) ) {
725+ if ( resolved ) {
707726 try {
708727 const log = await getLastSessionLog ( params . sessionId as UUID )
709728 if ( log && log . messages . length > 0 ) {
@@ -754,6 +773,37 @@ export class AcpAgent implements Agent {
754773 this . sessions . delete ( sessionId )
755774 }
756775
776+ /**
777+ * Load session history from disk and replay it to the ACP client.
778+ * Used when switching back to a session that is already in memory
779+ * (the client needs the conversation replayed to display it).
780+ */
781+ private async replaySessionHistory ( params : {
782+ sessionId : string
783+ cwd : string
784+ } ) : Promise < void > {
785+ try {
786+ const log = await getLastSessionLog ( params . sessionId as UUID )
787+ if ( ! log || log . messages . length === 0 ) return
788+ const messages = deserializeMessages ( log . messages )
789+ if ( messages . length === 0 ) return
790+
791+ const session = this . sessions . get ( params . sessionId )
792+ if ( ! session ) return
793+
794+ await replayHistoryMessages (
795+ params . sessionId ,
796+ messages as unknown as Array < Record < string , unknown > > ,
797+ this . conn ,
798+ session . toolUseCache ,
799+ this . clientCapabilities ,
800+ session . cwd ,
801+ )
802+ } catch ( err ) {
803+ console . error ( '[ACP] Failed to replay session history:' , err )
804+ }
805+ }
806+
757807 private applySessionMode ( sessionId : string , modeId : string ) : void {
758808 if ( ! isPermissionMode ( modeId ) ) {
759809 throw new Error ( `Invalid mode: ${ modeId } ` )
0 commit comments