@@ -14,6 +14,7 @@ import { Disposable, DisposableResourceMap, DisposableStore, IDisposable, Mutabl
1414import { revive } from '../../../../base/common/marshalling.js' ;
1515import { Schemas } from '../../../../base/common/network.js' ;
1616import { autorun , derived , IObservable } from '../../../../base/common/observable.js' ;
17+ import { isEqual } from '../../../../base/common/resources.js' ;
1718import { StopWatch } from '../../../../base/common/stopwatch.js' ;
1819import { isDefined } from '../../../../base/common/types.js' ;
1920import { URI } from '../../../../base/common/uri.js' ;
@@ -24,7 +25,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common
2425import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js' ;
2526import { ILogService } from '../../../../platform/log/common/log.js' ;
2627import { Progress } from '../../../../platform/progress/common/progress.js' ;
27- import { IStorageService , StorageScope , StorageTarget } from '../../../../platform/storage/common/storage.js' ;
28+ import { IStorageService , StorageScope } from '../../../../platform/storage/common/storage.js' ;
2829import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js' ;
2930import { IExtensionService } from '../../../services/extensions/common/extensions.js' ;
3031import { InlineChatConfigKeys } from '../../inlineChat/common/inlineChat.js' ;
@@ -36,10 +37,10 @@ import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, IChatModel, ICha
3637import { ChatModelStore , IStartSessionProps } from './chatModelStore.js' ;
3738import { chatAgentLeader , ChatRequestAgentPart , ChatRequestAgentSubcommandPart , ChatRequestSlashCommandPart , ChatRequestTextPart , chatSubcommandLeader , getPromptText , IParsedChatRequest } from './chatParserTypes.js' ;
3839import { ChatRequestParser } from './chatRequestParser.js' ;
39- import { ChatMcpServersStarting , IChatCompleteResponse , IChatDetail , IChatFollowup , IChatModelReference , IChatProgress , IChatSendRequestData , IChatSendRequestOptions , IChatSendRequestResponseState , IChatService , IChatSessionContext , IChatSessionStartOptions , IChatTransferredSessionData , IChatUserActionEvent , ResponseModelState } from './chatService.js' ;
40+ import { ChatMcpServersStarting , IChatCompleteResponse , IChatDetail , IChatFollowup , IChatModelReference , IChatProgress , IChatSendRequestData , IChatSendRequestOptions , IChatSendRequestResponseState , IChatService , IChatSessionContext , IChatSessionStartOptions , IChatUserActionEvent , ResponseModelState } from './chatService.js' ;
4041import { ChatRequestTelemetry , ChatServiceTelemetry } from './chatServiceTelemetry.js' ;
4142import { IChatSessionsService } from './chatSessionsService.js' ;
42- import { ChatSessionStore , IChatSessionEntryMetadata , IChatTransfer2 } from './chatSessionStore.js' ;
43+ import { ChatSessionStore , IChatSessionEntryMetadata } from './chatSessionStore.js' ;
4344import { IChatSlashCommandService } from './chatSlashCommands.js' ;
4445import { IChatTransferService } from './chatTransferService.js' ;
4546import { LocalChatSessionUri } from './chatUri.js' ;
@@ -50,10 +51,6 @@ import { ILanguageModelToolsService } from './languageModelToolsService.js';
5051
5152const serializedChatKey = 'interactive.sessions' ;
5253
53- const TransferredGlobalChatKey = 'chat.workspaceTransfer' ;
54-
55- const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60 ;
56-
5754class CancellableRequest implements IDisposable {
5855 constructor (
5956 public readonly cancellationTokenSource : CancellationTokenSource ,
@@ -82,9 +79,9 @@ export class ChatService extends Disposable implements IChatService {
8279 private _persistedSessions : ISerializableChatsData ;
8380 private _saveModelsEnabled = true ;
8481
85- private _transferredSessionData : IChatTransferredSessionData | undefined ;
86- public get transferredSessionData ( ) : IChatTransferredSessionData | undefined {
87- return this . _transferredSessionData ;
82+ private _transferredSessionResource : URI | undefined ;
83+ public get transferredSessionResource ( ) : URI | undefined {
84+ return this . _transferredSessionResource ;
8885 }
8986
9087 private readonly _onDidSubmitRequest = this . _register ( new Emitter < { readonly chatSessionResource : URI } > ( ) ) ;
@@ -128,7 +125,7 @@ export class ChatService extends Disposable implements IChatService {
128125 }
129126
130127 constructor (
131- @IStorageService private readonly storageService : IStorageService ,
128+ @IStorageService storageService : IStorageService ,
132129 @ILogService private readonly logService : ILogService ,
133130 @IExtensionService private readonly extensionService : IExtensionService ,
134131 @IInstantiationService private readonly instantiationService : IInstantiationService ,
@@ -175,21 +172,15 @@ export class ChatService extends Disposable implements IChatService {
175172 this . _persistedSessions = { } ;
176173 }
177174
178- const transferredData = this . getTransferredSessionData ( ) ;
179- const transferredChat = transferredData ?. chat ;
180- if ( transferredChat ) {
181- this . trace ( 'constructor' , `Transferred session ${ transferredChat . sessionId } ` ) ;
182- this . _persistedSessions [ transferredChat . sessionId ] = transferredChat ;
183- this . _transferredSessionData = {
184- sessionId : transferredChat . sessionId ,
185- location : transferredData . location ,
186- inputState : transferredData . inputState
187- } ;
188- }
189-
190175 this . _chatSessionStore = this . _register ( this . instantiationService . createInstance ( ChatSessionStore ) ) ;
191176 this . _chatSessionStore . migrateDataIfNeeded ( ( ) => this . _persistedSessions ) ;
192177
178+ const transferredData = this . _chatSessionStore . getTransferredSessionData ( ) ;
179+ if ( transferredData ) {
180+ this . trace ( 'constructor' , `Transferred session ${ transferredData } ` ) ;
181+ this . _transferredSessionResource = transferredData ;
182+ }
183+
193184 // When using file storage, populate _persistedSessions with session metadata from the index
194185 // This ensures that getPersistedSessionTitle() can find titles for inactive sessions
195186 this . initializePersistedSessionsFromFileStorage ( ) . then ( ( ) => {
@@ -309,23 +300,6 @@ export class ChatService extends Disposable implements IChatService {
309300 }
310301 }
311302
312- private getTransferredSessionData ( ) : IChatTransfer2 | undefined {
313- const data : IChatTransfer2 [ ] = this . storageService . getObject ( TransferredGlobalChatKey , StorageScope . PROFILE , [ ] ) ;
314- const workspaceUri = this . workspaceContextService . getWorkspace ( ) . folders [ 0 ] ?. uri ;
315- if ( ! workspaceUri ) {
316- return ;
317- }
318-
319- const thisWorkspace = workspaceUri . toString ( ) ;
320- const currentTime = Date . now ( ) ;
321- // Only use transferred data if it was created recently
322- const transferred = data . find ( item => URI . revive ( item . toWorkspace ) . toString ( ) === thisWorkspace && ( currentTime - item . timestampInMilliseconds < SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS ) ) ;
323- // Keep data that isn't for the current workspace and that hasn't expired yet
324- const filtered = data . filter ( item => URI . revive ( item . toWorkspace ) . toString ( ) !== thisWorkspace && ( currentTime - item . timestampInMilliseconds < SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS ) ) ;
325- this . storageService . store ( TransferredGlobalChatKey , JSON . stringify ( filtered ) , StorageScope . PROFILE , StorageTarget . MACHINE ) ;
326- return transferred ;
327- }
328-
329303 /**
330304 * todo@connor4312 This will be cleaned up with the globalization of edits.
331305 */
@@ -540,8 +514,9 @@ export class ChatService extends Disposable implements IChatService {
540514 }
541515
542516 let sessionData : ISerializableChatData | undefined ;
543- if ( this . transferredSessionData ?. sessionId === sessionId ) {
544- sessionData = revive ( this . _persistedSessions [ sessionId ] ) ;
517+ if ( isEqual ( this . transferredSessionResource , sessionResource ) ) {
518+ this . _transferredSessionResource = undefined ;
519+ sessionData = revive ( await this . _chatSessionStore . readTransferredSession ( sessionResource ) ) ;
545520 } else {
546521 sessionData = revive ( await this . _chatSessionStore . readSession ( sessionId ) ) ;
547522 }
@@ -558,11 +533,6 @@ export class ChatService extends Disposable implements IChatService {
558533 canUseTools : true ,
559534 } ) ;
560535
561- const isTransferred = this . transferredSessionData ?. sessionId === sessionId ;
562- if ( isTransferred ) {
563- this . _transferredSessionData = undefined ;
564- }
565-
566536 return sessionRef ;
567537 }
568538
@@ -1309,22 +1279,25 @@ export class ChatService extends Disposable implements IChatService {
13091279 return this . _chatSessionStore . hasSessions ( ) ;
13101280 }
13111281
1312- transferChatSession ( transferredSessionData : IChatTransferredSessionData , toWorkspace : URI ) : void {
1313- const model = Iterable . find ( this . _sessionModels . values ( ) , model => model . sessionId === transferredSessionData . sessionId ) ;
1282+ async transferChatSession ( transferredSessionResource : URI , toWorkspace : URI ) : Promise < void > {
1283+ if ( ! LocalChatSessionUri . isLocalSession ( transferredSessionResource ) ) {
1284+ throw new Error ( `Can only transfer local chat sessions. Invalid session: ${ transferredSessionResource } ` ) ;
1285+ }
1286+
1287+ const model = this . _sessionModels . get ( transferredSessionResource ) as ChatModel | undefined ;
13141288 if ( ! model ) {
1315- throw new Error ( `Failed to transfer session. Unknown session ID: ${ transferredSessionData . sessionId } ` ) ;
1289+ throw new Error ( `Failed to transfer session. Unknown session: ${ transferredSessionResource } ` ) ;
1290+ }
1291+
1292+ if ( model . initialLocation !== ChatAgentLocation . Chat ) {
1293+ throw new Error ( `Can only transfer chat sessions located in the Chat view. Session ${ transferredSessionResource } has location=${ model . initialLocation } ` ) ;
13161294 }
13171295
1318- const existingRaw : IChatTransfer2 [ ] = this . storageService . getObject ( TransferredGlobalChatKey , StorageScope . PROFILE , [ ] ) ;
1319- existingRaw . push ( {
1320- chat : model . toJSON ( ) ,
1296+ await this . _chatSessionStore . storeTransferSession ( {
1297+ sessionResource : model . sessionResource ,
13211298 timestampInMilliseconds : Date . now ( ) ,
13221299 toWorkspace : toWorkspace ,
1323- inputState : transferredSessionData . inputState ,
1324- location : transferredSessionData . location ,
1325- } ) ;
1326-
1327- this . storageService . store ( TransferredGlobalChatKey , JSON . stringify ( existingRaw ) , StorageScope . PROFILE , StorageTarget . MACHINE ) ;
1300+ } , model ) ;
13281301 this . chatTransferService . addWorkspaceToTransferred ( toWorkspace ) ;
13291302 this . trace ( 'transferChatSession' , `Transferred session ${ model . sessionResource } to workspace ${ toWorkspace . toString ( ) } ` ) ;
13301303 }
0 commit comments