chatSessionStore.ts ×41

Frontier kind: Code frontier

unlabeled · c_54ce42a41e58

103 tests · 43279 LOC · 188 files · introduces 0 tests · 427 LOC · 3 files

Introduces — evidence that enters the hierarchy at this concept

Code
47 ranges427 lines · 3 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
3899 ranges43279 lines · 188 files · Browse complete extent
All tests (intent)
103 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

3 files ranked by introduced lines: 427 introduced LOC across 47 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/model/chatSessionStore.ts 211 introduced LOC · 41 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatSessionStore.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Sequencer } from '../../../../../base/common/async.js';
7 > import { VSBuffer } from '../../../../../base/common/buffer.js';
8 > import { toErrorMessage } from '../../../../../base/common/errorMessage.js';
9 > import { MarkdownString } from '../../../../../base/common/htmlContent.js';
10 > import { Disposable } from '../../../../../base/common/lifecycle.js';
11 > import { revive } from '../../../../../base/common/marshalling.js';
12 > import { isEqual, joinPath } from '../../../../../base/common/resources.js';
13 > import { URI } from '../../../../../base/common/uri.js';
14 > import { localize } from '../../../../../nls.js';
15 > import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
16 > import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js';
17 > import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js';
18 > import { FileOperationResult, IFileService, toFileOperationResult } from '../../../../../platform/files/common/files.js';
19 > import { ILogService } from '../../../../../platform/log/common/log.js';
20 > import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
21 > import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
22 > import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
23 > import { IUserDataProfilesService } from '../../../../../platform/userDataProfile/common/userDataProfile.js';
24 > import { IAnyWorkspaceIdentifier, isEmptyWorkspaceIdentifier, IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
25 > import { Dto } from '../../../../services/extensions/common/proxyIdentifier.js';
26 > import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js';
27 > import { IWorkspaceEditingService } from '../../../../services/workspaces/common/workspaceEditing.js';
28 > import { awaitStatsForSession } from '../chat.js';
29 > import { IChatSessionStats, IChatSessionTiming, ResponseModelState } from '../chatService/chatService.js';
30 > import { ChatAgentLocation, ChatPermissionLevel } from '../constants.js';
31 > import { ModifiedFileEntryState } from '../editing/chatEditingService.js';
32 > import { ChatModel, ISerializableChatData, ISerializableChatDataIn, ISerializableChatModelInputState, ISerializableChatsData, ISerializedChatDataReference, normalizeSerializableChatData } from './chatModel.js';
33 > import { ChatSessionOperationLog } from './chatSessionOperationLog.js';
34 > import { LocalChatSessionUri } from './chatUri.js';
35 > import { stringifyEntryWithFallback } from './objectMutationLog.js';
36 >
37 > const maxPersistedSessions = 400;
38 >
39 > const ChatIndexStorageKey = 'chat.ChatSessionStore.index';
40 > const ChatTransferIndexStorageKey = 'ChatSessionStore.transferIndex';
41 >
42 > export class ChatSessionStore extends Disposable {
43 > private storageRoot: URI;
44 > private readonly previousEmptyWindowStorageRoot: URI | undefined;
45 > private readonly transferredSessionStorageRoot: URI;
46 >
47 > private readonly storeQueue = new Sequencer();
48 >
49 > private storeTask: Promise<void> | undefined;
50 > private shuttingDown = false;
51 >
52 > constructor(
53 @IFileService private readonly fileService: IFileService,
54 @IEnvironmentService private readonly environmentService: IEnvironmentService,
97 }));
98 }
100 > private async handleWorkspaceTransition(oldWorkspace: IAnyWorkspaceIdentifier, newWorkspace: IAnyWorkspaceIdentifier): Promise<void> {
101 const wasEmptyWindow = isEmptyWorkspaceIdentifier(oldWorkspace);
102 const isNewWorkspaceEmpty = isEmptyWorkspaceIdentifier(newWorkspace);
128 await this.migrateSessionsToNewWorkspace(oldStorageRoot, wasEmptyWindow, isNewWorkspaceEmpty);
129 }
131 > private async migrateSessionsToNewWorkspace(oldStorageRoot: URI, wasEmptyWindow: boolean, isNewWorkspaceEmpty: boolean): Promise<void> {
132 try {
133 // Check if old storage location exists
182 }
183 }
185 > async storeSessions(sessions: ChatModel[]): Promise<void> {
186 if (this.shuttingDown) {
187 // Don't start this task if we missed the chance to block shutdown
204 }
205 }
207 > async storeSessionsMetadataOnly(sessions: ChatModel[]): Promise<void> {
208 if (this.shuttingDown) {
209 // Don't start this task if we missed the chance to block shutdown
225 }
226 }
228 > async storeTransferSession(transferData: IChatTransfer, session: ChatModel): Promise<void> {
229 const index = this.getTransferredSessionIndex();
230 const workspaceKey = transferData.toWorkspace.toString();
262 }
263 }
265 > private getTransferredSessionIndex(): IChatTransferIndex {
266 try {
267 const data: IChatTransferIndex = this.storageService.getObject(ChatTransferIndexStorageKey, StorageScope.PROFILE, {});
272 }
273 }
275 > private static readonly TRANSFER_EXPIRATION_MS = 60 * 1000 * 5;
276 >
277 > getTransferredSessionData(): URI | undefined {
278 try {
279 const index = this.getTransferredSessionIndex();
303 }
304 }
306 > async readTransferredSession(sessionResource: URI): Promise<ISerializedChatDataReference | undefined> {
307 try {
308 const storageLocation = this.getTransferredSessionStorageLocation(sessionResource);
323 }
324 }
326 > private async cleanupTransferredSession(sessionResource: URI): Promise<void> {
327 try {
328 // Remove from index
344 }
345 }
347 > private _didReportIssue = false;
348 >
349 > private async writeSession(session: ChatModel | ISerializableChatData): Promise<void> {
350 try {
351 const index = this.internalGetIndex();
402 }
403 }
405 > private async writeSessionMetadataOnly(session: ChatModel): Promise<void> {
406 // Only to be used for external sessions
407 if (LocalChatSessionUri.parseLocalSessionId(session.sessionResource)) {
419 }
420 }
422 > private async flushIndex(): Promise<void> {
423 const index = this.internalGetIndex();
424 try {
429 }
430 }
432 > private getIndexStorageScope(): StorageScope {
433 const workspace = this.workspaceContextService.getWorkspace();
434 const isEmptyWindow = !workspace.configuration && workspace.folders.length === 0;
435 return isEmptyWindow ? StorageScope.APPLICATION : StorageScope.WORKSPACE;
436 }
438 > private async trimEntries(): Promise<void> {
439 const index = this.internalGetIndex();
440 const entries = Object.entries(index.entries)
452 }
453 }
455 > private async internalDeleteSession(sessionId: string): Promise<void> {
456 const index = this.internalGetIndex();
457 if (!index.entries[sessionId]) {
474 }
475 }
477 > hasSessions(): boolean {
478 return Object.keys(this.internalGetIndex().entries).length > 0;
479 }
481 > isSessionEmpty(sessionId: string): boolean {
482 const index = this.internalGetIndex();
483 return index.entries[sessionId]?.isEmpty ?? true;
484 }
486 > async deleteSession(sessionId: string): Promise<void> {
487 await this.storeQueue.queue(async () => {
488 await this.internalDeleteSession(sessionId);
490 });
491 }
493 > async clearAllSessions(): Promise<void> {
494 await this.storeQueue.queue(async () => {
495 const index = this.internalGetIndex();
500 });
501 }
503 > public async setSessionTitle(sessionId: string, title: string): Promise<void> {
504 await this.storeQueue.queue(async () => {
505 const index = this.internalGetIndex();
509 });
510 }
512 > private reportError(reasonForTelemetry: string, message: string, error?: Error): void {
513 const fileOperationReason = error && toFileOperationResult(error);
514
537 });
538 }
540 > private indexCache: IChatSessionIndexData | undefined;
541 > private internalGetIndex(): IChatSessionIndexData {
542 if (this.indexCache) {
543 return this.indexCache;
580 return this.indexCache;
581 }
583 > async getIndex(): Promise<IChatSessionIndex> {
584 return this.storeQueue.queue(async () => {
585 return this.internalGetIndex().entries;
586 });
587 }
589 > getMetadataForSessionSync(sessionResource: URI): IChatSessionEntryMetadata | undefined {
590 const index = this.internalGetIndex();
591 return index.entries[this.getIndexKey(sessionResource)];
592 }
594 > private getIndexKey(sessionResource: URI): string {
595 const sessionId = LocalChatSessionUri.parseLocalSessionId(sessionResource);
596 return sessionId ?? sessionResource.toString();
597 }
599 > logIndex(): void {
600 const data = this.storageService.get(ChatIndexStorageKey, this.getIndexStorageScope(), undefined);
601 this.logService.info('ChatSessionStore index: ', data);
602 }
604 > async migrateDataIfNeeded(getInitialData: () => ISerializableChatsData | undefined): Promise<void> {
605 await this.storeQueue.queue(async () => {
606 const data = this.storageService.get(ChatIndexStorageKey, this.getIndexStorageScope(), undefined);
614 });
615 }
617 > private async migrate(initialData: ISerializableChatsData): Promise<void> {
618 const numSessions = Object.keys(initialData).length;
619 this.logService.info(`ChatSessionStore: Migrating ${numSessions} chat sessions from storage service to file system`);
625 await this.flushIndex();
626 }
628 > public async readSession(sessionId: string): Promise<ISerializedChatDataReference | undefined> {
629 return await this.storeQueue.queue(async () => {
630 const storageLocation = this.getStorageLocation(sessionId);
632 });
633 }
635 > private async readSessionFromLocation(flatStorageLocation: URI, logStorageLocation: URI | undefined, sessionId: string): Promise<ISerializedChatDataReference | undefined> {
636 let fromLocation = flatStorageLocation;
637 let rawData: VSBuffer | undefined;
693 }
694 }
696 > private async readSessionFromPreviousLocation(sessionId: string): Promise<VSBuffer | undefined> {
697 let rawData: VSBuffer | undefined;
698
710 return rawData;
711 }
713 > private getStorageLocation(chatSessionId: string): {
714 /** <1.109 flat JSON file */
715 flat: URI;
723 };
724 }
726 > private getTransferredSessionStorageLocation(sessionResource: URI): URI {
727 const sessionId = LocalChatSessionUri.parseLocalSessionId(sessionResource);
728 return joinPath(this.transferredSessionStorageRoot, `${sessionId}.json`);
729 }
731 > /**
732 > * Synchronously update the in-memory index entries for the given sessions
733 > * and flush the index to storage. This ensures the index is persisted
734 > * even when called from a synchronous `onWillSaveState` handler where
735 > * async file-write work would complete after the storage service has
736 > * already flushed.
737 > */
738 > updateAndFlushIndexSync(localSessions: ChatModel[], externalSessions: ChatModel[]): void {
739 const index = this.internalGetIndex();
740 for (const session of localSessions) {
751 }
752 }
754 > public getChatStorageFolder(): URI {
755 return this.storageRoot;
756 }
758 >
759 > export interface IChatSessionEntryMetadata {
760 > sessionId: string;
761 > title: string;
762 > lastMessageDate: number;
763 > timing: IChatSessionTiming;
764 > initialLocation?: ChatAgentLocation;
765 > hasPendingEdits?: boolean;
766 > stats?: IChatSessionStats;
767 > lastResponseState: ResponseModelState;
768 >
769 > /**
770 > * The working directory URI string associated with this session.
771 > * Persisted so it survives window reload in the agents/sessions window.
772 > */
773 > workingDirectory?: string;
774 >
775 > /**
776 > * This only exists because the migrated data from the storage service had empty sessions persisted, and it's impossible to know which ones are
777 > * currently in use. Now, `clearSession` deletes empty sessions, so old ones shouldn't take up space in the store anymore, but we still need to
778 > * filter the old ones out of history.
779 > */
780 > isEmpty?: boolean;
781 >
782 > /**
783 > * Whether this session was loaded from an external provider (eg background/cloud sessions).
784 > */
785 > isExternal?: boolean;
786 >
787 > /**
788 > * The permission level for tool auto-approval, if not default.
789 > */
790 > permissionLevel?: ChatPermissionLevel;
791 >
792 > /**
793 > * Serialized draft input state (text, attachments, mode, selected model, ...) for
794 > * external sessions, so that unsent input is preserved when switching away and
795 > * back. Local sessions instead persist their full state via storeSessions.
796 > */
797 > inputState?: ISerializableChatModelInputState;
798 > }
799 >
800 function isChatSessionEntryMetadata(obj: unknown): obj is IChatSessionEntryMetadata {
801 return (
807 );
808 }
810 > export type IChatSessionIndex = Record<string, IChatSessionEntryMetadata>;
811 >
812 > interface IChatSessionIndexData {
813 > version: 1;
814 > entries: IChatSessionIndex;
815 > }
816 >
817 > // TODO if we update the index version:
818 > // Don't throw away index when moving backwards in VS Code version. Try to recover it. But this scenario is hard.
819 function isChatSessionIndex(data: unknown): data is IChatSessionIndexData {
820 if (typeof data !== 'object' || data === null) {
839 return true;
840 }
842 > /**
843 > * Builds session metadata synchronously from a live ChatModel.
844 > * Used both by {@link updateAndFlushIndexSync} (where async work is not
845 > * possible) and by {@link getSessionMetadata} (which layers on async stats).
846 > */
847 function getSessionMetadataSync(session: ChatModel): IChatSessionEntryMetadata {
848 const title = session.customTitle || session.title;
872 };
873 }
875 async function getSessionMetadata(session: ChatModel | ISerializableChatData): Promise<IChatSessionEntryMetadata> {
876 if (session instanceof ChatModel) {
899 };
900 }
902 > export interface IChatTransfer {
903 > toWorkspace: URI;
904 > sessionResource: URI;
905 > timestampInMilliseconds: number;
906 > }
907 >
908 > export interface IChatTransfer2 extends IChatTransfer {
909 > chat: ISerializableChatData;
910 > }
911 >
912 > type IChatTransferDto = Dto<IChatTransfer>;
913 >
914 > /**
915 > * Map of destination workspace URI to chat transfer data
916 > */
917 > type IChatTransferIndex = Record<string, IChatTransferDto>;
src/vs/workbench/contrib/chat/common/model/chatSessionOperationLog.ts 136 introduced LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatSessionOperationLog.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { softAssertNever } from '../../../../../base/common/assert.js';
7 > import { isMarkdownString } from '../../../../../base/common/htmlContent.js';
8 > import { equals as objectsEqual } from '../../../../../base/common/objects.js';
9 > import { isEqual as _urisEqual } from '../../../../../base/common/resources.js';
10 > import { hasKey } from '../../../../../base/common/types.js';
11 > import { URI, UriComponents } from '../../../../../base/common/uri.js';
12 > import { IChatRequestVariableEntry } from '../attachments/chatVariableEntries.js';
13 > import { IChatMarkdownContent, IChatMcpAuthenticationRequired, IChatMcpServersStartingSlow, ResponseModelState } from '../chatService/chatService.js';
14 > import { ModifiedFileEntryState } from '../editing/chatEditingService.js';
15 > import { IParsedChatRequest } from '../requestParser/chatParserTypes.js';
16 > import { IChatAgentEditedFileEvent, IChatDataSerializerLog, IChatModel, IChatPendingRequest, IChatProgressResponseContent, IChatRequestModel, IChatRequestVariableData, ISerializableChatData, ISerializableChatModelInputState, ISerializableChatRequestData, ISerializablePendingRequestData, SerializedChatResponsePart, serializeSendOptions } from './chatModel.js';
17 > import * as Adapt from './objectMutationLog.js';
18 >
19 > /**
20 > * ChatModel has lots of properties and lots of ways those properties can mutate.
21 > * The naive way to store the ChatModel is serializing it to JSON and calling it
22 > * a day. However, chats can get very, very long, and thus doing so is slow.
23 > *
24 > * In this file, we define a `storageSchema` that adapters from the `IChatModel`
25 > * into the serializable format. This schema tells us what properties in the chat
26 > * model correspond to the serialized properties, *and how they change*. For
27 > * example, `Adapt.constant(...)` defines a property that will never be checked
28 > * for changes after it's written, and `Adapt.primitive(...)` defines a property
29 > * that will be checked for changes using strict equality each time we store it.
30 > *
31 > * We can then use this to generate a log of mutations that we can append to
32 > * cheaply without rewriting and reserializing the entire request each time.
33 > */
34 >
35 > const toJson = <T>(obj: T): T extends { toJSON?(): infer R } ? R : T => {
36 const cast = obj as { toJSON?: () => T };
37 // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
38 return (cast && typeof cast.toJSON === 'function' ? cast.toJSON() : obj) as any;
39 };
41 > const responsePartSchema = Adapt.v<Exclude<IChatProgressResponseContent, IChatMcpAuthenticationRequired | IChatMcpServersStartingSlow>, SerializedChatResponsePart>(
42 > (obj): SerializedChatResponsePart => obj.kind === 'markdownContent' ? obj.content : toJson(obj),
43 > (a, b) => {
44 if (isMarkdownString(a) && isMarkdownString(b)) {
45 return a.value === b.value;
106 return false;
107 }
109 >
110 > const urisEqual = (a: UriComponents, b: UriComponents): boolean => {
111 return _urisEqual(URI.from(a), URI.from(b));
112 };
114 > const messageSchema = Adapt.object<IParsedChatRequest, IParsedChatRequest>({
115 > text: Adapt.v(m => m.text),
116 > parts: Adapt.v(m => m.parts, (a, b) => a.length === b.length && a.every((part, i) => part.text === b[i].text)),
117 > });
118 >
119 > const agentEditedFileEventSchema = Adapt.object<IChatAgentEditedFileEvent, IChatAgentEditedFileEvent>({
120 > uri: Adapt.v(e => e.uri, urisEqual),
121 > eventKind: Adapt.v(e => e.eventKind),
122 > });
123 >
124 > const chatVariableSchema = Adapt.object<IChatRequestVariableData, IChatRequestVariableData>({
125 > variables: Adapt.t(v => v.variables.map(IChatRequestVariableEntry.toExport), Adapt.array(Adapt.value((a, b) => a.name === b.name))),
126 > });
127 >
128 > const requestSchema = Adapt.object<IChatRequestModel, ISerializableChatRequestData>({
129 > // request parts
130 > requestId: Adapt.t(m => m.id, Adapt.key()),
131 > timestamp: Adapt.v(m => m.requestTimestamp),
132 > confirmation: Adapt.v(m => m.confirmation),
133 > message: Adapt.t(m => m.message, messageSchema),
134 > shouldBeRemovedOnSend: Adapt.v(m => m.shouldBeRemovedOnSend, objectsEqual),
135 > agent: Adapt.v(m => m.response?.agent, (a, b) => a?.id === b?.id),
136 > modelId: Adapt.v(m => m.modelId),
137 > editedFileEvents: Adapt.t(m => m.editedFileEvents, Adapt.array(agentEditedFileEventSchema)),
138 > variableData: Adapt.t(m => m.variableData, chatVariableSchema),
139 > isHidden: Adapt.v(() => undefined), // deprecated, always undefined for new data
140 > isCanceled: Adapt.v(() => undefined), // deprecated, modelState is used instead
141 >
142 > response: Adapt.t(m => m.response?.entireResponse.value.filter((p): p is Exclude<IChatProgressResponseContent, IChatMcpAuthenticationRequired | IChatMcpServersStartingSlow> => p.kind !== 'mcpAuthenticationRequired' && p.kind !== 'mcpServersStartingSlow'), Adapt.array(responsePartSchema)),
143 > responseId: Adapt.v(m => m.response?.id),
144 > responseTimestamp: Adapt.v(m => m.response?.timestamp),
145 > result: Adapt.v(m => m.response?.result, objectsEqual),
146 > responseMarkdownInfo: Adapt.v(
147 > m => m.response?.codeBlockInfos?.map(info => ({ suggestionId: info.suggestionId })),
148 > objectsEqual,
149 > ),
150 > followups: Adapt.v(m => m.response?.followups, objectsEqual),
151 > modelState: Adapt.v(m => m.response?.stateT, objectsEqual),
152 > vote: Adapt.v(m => m.response?.vote),
153 > slashCommand: Adapt.t(m => m.response?.slashCommand, Adapt.value((a, b) => a?.name === b?.name)),
154 > usedContext: Adapt.v(m => m.response?.usedContext, objectsEqual),
155 > contentReferences: Adapt.v(m => m.response?.contentReferences, objectsEqual),
156 > codeCitations: Adapt.v(m => m.response?.codeCitations, objectsEqual),
157 > timeSpentWaiting: Adapt.v(m => m.response?.timestamp), // based on response timestamp
158 > completionTokens: Adapt.v(m => m.response?.completionTokenCount),
159 > promptTokens: Adapt.v(m => m.response?.usage?.promptTokens),
160 > outputBuffer: Adapt.v(m => m.response?.usage?.outputBuffer),
161 > promptTokenDetails: Adapt.v(m => m.response?.usage?.promptTokenDetails, objectsEqual),
162 > copilotCredits: Adapt.v(m => m.response?.usage?.copilotCredits),
163 > elapsedMs: Adapt.v(m => m.response?.elapsedMs ?? (m.response?.completedAt ? Math.max(0, m.response.completedAt - m.response.confirmationAdjustedTimestamp.get()) : undefined)),
164 > modeInfo: Adapt.v(m => m.modeInfo, objectsEqual),
165 > isSystemInitiated: Adapt.v(m => m.isSystemInitiated),
166 > systemInitiatedLabel: Adapt.v(m => m.systemInitiatedLabel),
167 > terminalExecutionId: Adapt.v(m => m.terminalExecutionId),
168 > }, {
169 > sealed: (o) => o.modelState?.value === ResponseModelState.Cancelled || o.modelState?.value === ResponseModelState.Failed || o.modelState?.value === ResponseModelState.Complete,
170 > });
171 >
172 > const inputStateSchema = Adapt.object<ISerializableChatModelInputState, ISerializableChatModelInputState>({
173 > attachments: Adapt.v(i => i.attachments.map(IChatRequestVariableEntry.toExport), objectsEqual),
174 > mode: Adapt.v(i => i.mode, (a, b) => a.id === b.id),
175 > selectedModel: Adapt.v(i => i.selectedModel, (a, b) => a?.identifier === b?.identifier && objectsEqual(a?.modelConfiguration, b?.modelConfiguration)),
176 > inputText: Adapt.v(i => i.inputText),
177 > selections: Adapt.v(i => i.selections, objectsEqual),
178 > permissionLevel: Adapt.v(i => i.permissionLevel),
179 > contrib: Adapt.v(i => i.contrib, objectsEqual),
180 > });
181 >
182 > const pendingRequestSchema = Adapt.object<IChatPendingRequest, ISerializablePendingRequestData>({
183 > id: Adapt.t(p => p.request.id, Adapt.key()),
184 > request: Adapt.t(p => p.request, requestSchema),
185 > kind: Adapt.v(p => p.kind),
186 > sendOptions: Adapt.v(p => serializeSendOptions(p.sendOptions), objectsEqual),
187 > });
188 >
189 > export const storageSchema = Adapt.object<IChatModel, ISerializableChatData>({
190 > version: Adapt.v(() => 3),
191 > creationDate: Adapt.v(m => m.timestamp),
192 > customTitle: Adapt.v(m => m.hasCustomTitle ? m.title : undefined),
193 > initialLocation: Adapt.v(m => m.initialLocation),
194 > inputState: Adapt.t(m => m.inputModel.toJSON(), inputStateSchema),
195 > responderUsername: Adapt.v(m => m.responderUsername),
196 > sessionId: Adapt.v(m => m.sessionId),
197 > requests: Adapt.t(m => m.getRequests(), Adapt.array(requestSchema)),
198 > hasPendingEdits: Adapt.v(m => m.editingSession?.entries.get().some(e => e.state.get() === ModifiedFileEntryState.Modified)),
199 > repoData: Adapt.v(m => m.repoData, objectsEqual),
200 > pendingRequests: Adapt.t(m => m.getPendingRequests(), Adapt.array(pendingRequestSchema)),
201 > workingDirectory: Adapt.v(m => m.workingDirectory?.toString()),
202 > });
203 >
204 > export class ChatSessionOperationLog extends Adapt.ObjectMutationLog<IChatModel, ISerializableChatData> implements IChatDataSerializerLog {
205 > constructor() {
206 super(storageSchema, 1024);
207 }
src/vs/workbench/services/workspaces/common/workspaceEditing.ts 80 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspaceEditing.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../../base/common/event.js';
7 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
8 > import { IWorkspaceFolderCreationData } from '../../../../platform/workspaces/common/workspaces.js';
9 > import { URI } from '../../../../base/common/uri.js';
10 > import { IAnyWorkspaceIdentifier, IWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';
11 >
12 > export const IWorkspaceEditingService = createDecorator<IWorkspaceEditingService>('workspaceEditingService');
13 >
14 > /**
15 > * An event that is fired after entering a workspace. Clients can join the entering
16 > * by providing a promise from the join method. This allows for long running operations
17 > * to complete (e.g. to migrate data into the new workspace) before the workspace
18 > * is fully entered.
19 > */
20 > export interface IDidEnterWorkspaceEvent {
21 > readonly oldWorkspace: IAnyWorkspaceIdentifier;
22 > readonly newWorkspace: IAnyWorkspaceIdentifier;
23 >
24 > join(promise: Promise<void>): void;
25 > }
26 >
27 > export interface IWorkspaceEditingService {
28 >
29 > readonly _serviceBrand: undefined;
30 >
31 > /**
32 > * Fired after the workspace is entered. Allows listeners to join the
33 > * entering with a promise to migrate data into this new workspace.
34 > */
35 > readonly onDidEnterWorkspace: Event<IDidEnterWorkspaceEvent>;
36 >
37 > /**
38 > * Add folders to the existing workspace.
39 > * When `donotNotifyError` is `true`, error will be bubbled up otherwise, the service handles the error with proper message and action
40 > */
41 > addFolders(folders: IWorkspaceFolderCreationData[], donotNotifyError?: boolean): Promise<void>;
42 >
43 > /**
44 > * Remove folders from the existing workspace
45 > * When `donotNotifyError` is `true`, error will be bubbled up otherwise, the service handles the error with proper message and action
46 > */
47 > removeFolders(folders: URI[], donotNotifyError?: boolean): Promise<void>;
48 >
49 > /**
50 > * Allows to add and remove folders to the existing workspace at once.
51 > * When `donotNotifyError` is `true`, error will be bubbled up otherwise, the service handles the error with proper message and action
52 > */
53 > updateFolders(index: number, deleteCount?: number, foldersToAdd?: IWorkspaceFolderCreationData[], donotNotifyError?: boolean): Promise<void>;
54 >
55 > /**
56 > * Enters the workspace with the provided path.
57 > */
58 > enterWorkspace(path: URI): Promise<void>;
59 >
60 > /**
61 > * Creates a new workspace with the provided folders and opens it. if path is provided
62 > * the workspace will be saved into that location.
63 > */
64 > createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise<void>;
65 >
66 > /**
67 > * Saves the current workspace to the provided path and opens it. requires a workspace to be opened.
68 > */
69 > saveAndEnterWorkspace(path: URI): Promise<void>;
70 >
71 > /**
72 > * Copies current workspace settings to the target workspace.
73 > */
74 > copyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): Promise<void>;
75 >
76 > /**
77 > * Picks a new workspace path
78 > */
79 > pickNewWorkspacePath(): Promise<URI | undefined>;
80 > }