Skip to content

Commit c15e31f

Browse files
authored
"import chat" fixes (#280089)
Fix #258517
1 parent 62b141e commit c15e31f

File tree

6 files changed

+257
-32
lines changed

6 files changed

+257
-32
lines changed

src/vs/workbench/contrib/chat/browser/actions/chatImportExport.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ChatContextKeys } from '../../common/chatContextKeys.js';
1818
import { isExportableSessionData } from '../../common/chatModel.js';
1919
import { IChatService } from '../../common/chatService.js';
2020
import { URI } from '../../../../../base/common/uri.js';
21+
import { revive } from '../../../../../base/common/marshalling.js';
2122

2223
const defaultFileName = 'chat.json';
2324
const filters = [{ name: localize('chat.file.label', "Chat Session"), extensions: ['json'] }];
@@ -94,7 +95,7 @@ export function registerChatExportActions() {
9495

9596
const content = await fileService.readFile(result[0]);
9697
try {
97-
const data = JSON.parse(content.value.toString());
98+
const data = revive(JSON.parse(content.value.toString()));
9899
if (!isExportableSessionData(data)) {
99100
throw new Error('Invalid chat session data');
100101
}

src/vs/workbench/contrib/chat/common/chatModel.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { ISelection } from '../../../../editor/common/core/selection.js';
2525
import { TextEdit } from '../../../../editor/common/languages.js';
2626
import { EditSuggestionId } from '../../../../editor/common/textModelEditSource.js';
2727
import { localize } from '../../../../nls.js';
28-
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
2928
import { ILogService } from '../../../../platform/log/common/log.js';
3029
import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommon.js';
3130
import { migrateLegacyTerminalToolSpecificData } from './chat.js';
@@ -1239,7 +1238,6 @@ export interface IExportableChatData {
12391238
export interface ISerializableChatData1 extends IExportableChatData {
12401239
sessionId: string;
12411240
creationDate: number;
1242-
isImported: boolean;
12431241

12441242
/** Indicates that this session was created in this window. Is cleared after the chat has been written to storage once. Needed to sync chat creations/deletions between empty windows. */
12451243
isNew?: boolean;
@@ -1400,8 +1398,9 @@ function getLastYearDate(): number {
14001398
}
14011399

14021400
export function isExportableSessionData(obj: unknown): obj is IExportableChatData {
1403-
const data = obj as IExportableChatData;
1404-
return typeof data === 'object';
1401+
return !!obj &&
1402+
Array.isArray((obj as IExportableChatData).requests) &&
1403+
typeof (obj as IExportableChatData).responderUsername === 'string';
14051404
}
14061405

14071406
export function isSerializableSessionData(obj: unknown): obj is ISerializableChatData {
@@ -1652,26 +1651,26 @@ export class ChatModel extends Disposable implements IChatModel {
16521651
@IChatAgentService private readonly chatAgentService: IChatAgentService,
16531652
@IChatEditingService private readonly chatEditingService: IChatEditingService,
16541653
@IChatService chatService: IChatService,
1655-
@IConfigurationService configurationService: IConfigurationService,
16561654
) {
16571655
super();
16581656

1659-
const isValid = isSerializableSessionData(initialData);
1660-
if (initialData && !isValid) {
1657+
const isValidExportedData = isExportableSessionData(initialData);
1658+
const isValidFullData = isValidExportedData && isSerializableSessionData(initialData);
1659+
if (initialData && !isValidExportedData) {
16611660
this.logService.warn(`ChatModel#constructor: Loaded malformed session data: ${JSON.stringify(initialData)}`);
16621661
}
16631662

1664-
this._isImported = (!!initialData && !isValid) || (initialData?.isImported ?? false);
1665-
this._sessionId = (isValid && initialData.sessionId) || initialModelProps.sessionId || generateUuid();
1663+
this._isImported = !!initialData && isValidExportedData && !isValidFullData;
1664+
this._sessionId = (isValidFullData && initialData.sessionId) || initialModelProps.sessionId || generateUuid();
16661665
this._sessionResource = initialModelProps.resource ?? LocalChatSessionUri.forSession(this._sessionId);
16671666

16681667
this._requests = initialData ? this._deserialize(initialData) : [];
1669-
this._timestamp = (isValid && initialData.creationDate) || Date.now();
1670-
this._lastMessageDate = (isValid && initialData.lastMessageDate) || this._timestamp;
1671-
this._customTitle = isValid ? initialData.customTitle : undefined;
1668+
this._timestamp = (isValidFullData && initialData.creationDate) || Date.now();
1669+
this._lastMessageDate = (isValidFullData && initialData.lastMessageDate) || this._timestamp;
1670+
this._customTitle = isValidFullData ? initialData.customTitle : undefined;
16721671

16731672
// Initialize input model from serialized data (undefined for new chats)
1674-
const serializedInputState = isValid && initialData.inputState ? initialData.inputState : undefined;
1673+
const serializedInputState = isValidFullData && initialData.inputState ? initialData.inputState : undefined;
16751674
this.inputModel = new InputModel(serializedInputState && {
16761675
attachments: serializedInputState.attachments,
16771676
mode: serializedInputState.mode,
@@ -2121,7 +2120,6 @@ export class ChatModel extends Disposable implements IChatModel {
21212120
...this.toExport(),
21222121
sessionId: this.sessionId,
21232122
creationDate: this._timestamp,
2124-
isImported: this._isImported,
21252123
lastMessageDate: this._lastMessageDate,
21262124
customTitle: this._customTitle,
21272125
// Only include inputState if it has been set

src/vs/workbench/contrib/chat/common/chatServiceImpl.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class ChatService extends Disposable implements IChatService {
168168
createModel: (props: IStartSessionProps) => this._startSession(props),
169169
willDisposeModel: async (model: ChatModel) => {
170170
const localSessionId = LocalChatSessionUri.parseLocalSessionId(model.sessionResource);
171-
if (localSessionId && (model.initialLocation === ChatAgentLocation.Chat)) {
171+
if (localSessionId && this.shouldStoreSession(model)) {
172172
// Always preserve sessions that have custom titles, even if empty
173173
if (model.getRequests().length === 0 && !model.customTitle) {
174174
await this._chatSessionStore.deleteSession(localSessionId);
@@ -234,16 +234,21 @@ export class ChatService extends Disposable implements IChatService {
234234

235235
private saveState(): void {
236236
const liveChats = Array.from(this._sessionModels.values())
237-
.filter(session => {
238-
if (!LocalChatSessionUri.parseLocalSessionId(session.sessionResource)) {
239-
return false;
240-
}
241-
return session.initialLocation === ChatAgentLocation.Chat;
242-
});
237+
.filter(session => this.shouldStoreSession(session));
243238

244239
this._chatSessionStore.storeSessions(liveChats);
245240
}
246241

242+
/**
243+
* Only persist local sessions from chat that are not imported.
244+
*/
245+
private shouldStoreSession(session: ChatModel): boolean {
246+
if (!LocalChatSessionUri.parseLocalSessionId(session.sessionResource)) {
247+
return false;
248+
}
249+
return session.initialLocation === ChatAgentLocation.Chat && !session.isImported;
250+
}
251+
247252
notifyUserAction(action: IChatUserActionEvent): void {
248253
this._chatServiceTelemetry.notifyUserAction(action);
249254
this._onDidPerformUserAction.fire(action);
@@ -345,7 +350,6 @@ export class ChatService extends Disposable implements IChatService {
345350
customTitle: metadata.title,
346351
creationDate: Date.now(), // Use current time as fallback
347352
lastMessageDate: metadata.lastMessageDate,
348-
isImported: metadata.isImported || false,
349353
initialLocation: metadata.initialLocation,
350354
requests: [], // Empty requests array - this is just for title lookup
351355
responderUsername: '',

src/vs/workbench/contrib/chat/common/chatSessionStore.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,11 +383,10 @@ export class ChatSessionStore extends Disposable {
383383
}
384384
}
385385

386-
interface IChatSessionEntryMetadata {
386+
export interface IChatSessionEntryMetadata {
387387
sessionId: string;
388388
title: string;
389389
lastMessageDate: number;
390-
isImported?: boolean;
391390
initialLocation?: ChatAgentLocation;
392391

393392
/**
@@ -447,7 +446,6 @@ function getSessionMetadata(session: ChatModel | ISerializableChatData): IChatSe
447446
sessionId: session.sessionId,
448447
title: title || localize('newChat', "New Chat"),
449448
lastMessageDate: session.lastMessageDate,
450-
isImported: session.isImported,
451449
initialLocation: session.initialLocation,
452450
isEmpty: session instanceof ChatModel ? session.getRequests().length === 0 : session.requests.length === 0
453451
};

0 commit comments

Comments
 (0)