Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/agentHost/common/sessionDataService.ts
13394 views
1
/*---------------------------------------------------------------------------------------------
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 { IDisposable, IReference } from '../../../base/common/lifecycle.js';
7
import { URI } from '../../../base/common/uri.js';
8
import { createDecorator } from '../../instantiation/common/instantiation.js';
9
import type { FileEditKind } from './state/sessionState.js';
10
11
export const ISessionDataService = createDecorator<ISessionDataService>('sessionDataService');
12
13
/** Filename of the per-session SQLite database. */
14
export const SESSION_DB_FILENAME = 'session.db';
15
16
// ---- File-edit types ----------------------------------------------------
17
18
/**
19
* Lightweight metadata for a file edit. Returned by {@link ISessionDatabase.getFileEdits}
20
* without the (potentially large) file content blobs.
21
*/
22
export interface IFileEditRecord {
23
/** The turn that owns this file edit. */
24
turnId: string;
25
/** The tool call that produced this edit. */
26
toolCallId: string;
27
/** Primary file path (after-path for edits/creates/renames, before-path for deletes). */
28
filePath: string;
29
/** The kind of file operation. */
30
kind: FileEditKind;
31
/** For renames, the original file path before the move. */
32
originalPath?: string;
33
/** Number of lines added (informational, for diff metadata). */
34
addedLines: number | undefined;
35
/** Number of lines removed (informational, for diff metadata). */
36
removedLines: number | undefined;
37
}
38
39
/**
40
* The before/after content blobs for a single file edit.
41
* Retrieved on demand via {@link ISessionDatabase.readFileEditContent}.
42
*
43
* For creates, `beforeContent` is absent.
44
* For deletes, `afterContent` is absent.
45
*/
46
export interface IFileEditContent {
47
/** File content before the edit. Absent for file creations. */
48
beforeContent?: Uint8Array;
49
/** File content after the edit. Absent for file deletions. */
50
afterContent?: Uint8Array;
51
}
52
53
// ---- Session database ---------------------------------------------------
54
55
/**
56
* A disposable handle to a per-session SQLite database backed by
57
* `@vscode/sqlite3`.
58
*
59
* Callers obtain an instance via {@link ISessionDataService.openDatabase} and
60
* **must** dispose it when finished to close the underlying database connection.
61
*/
62
export interface ISessionDatabase extends IDisposable {
63
/**
64
* Create a turn record. Must be called before storing file edits that
65
* reference this turn.
66
*/
67
createTurn(turnId: string): Promise<void>;
68
69
/**
70
* Delete a turn and all of its associated file edits (cascade).
71
*/
72
deleteTurn(turnId: string): Promise<void>;
73
74
/**
75
* Associates a Copilot SDK event ID with a turn. The event ID corresponds
76
* to the `user.message` event in the SDK event stream and is used by
77
* the SDK's `history.truncate` and `sessions.fork` RPCs.
78
*/
79
setTurnEventId(turnId: string, eventId: string): Promise<void>;
80
81
/**
82
* Retrieves the SDK event ID previously stored for a turn.
83
* Returns `undefined` if no event ID has been set.
84
*/
85
getTurnEventId(turnId: string): Promise<string | undefined>;
86
87
/**
88
* Returns the SDK event ID of the turn inserted immediately after the
89
* given turn, or `undefined` if the given turn is the last one.
90
*/
91
getNextTurnEventId(turnId: string): Promise<string | undefined>;
92
93
/**
94
* Returns the SDK event ID of the earliest turn in insertion order,
95
* or `undefined` if there are no turns.
96
*/
97
getFirstTurnEventId(): Promise<string | undefined>;
98
99
/**
100
* Deletes the given turn and all turns inserted after it, along
101
* with their associated file edits (cascade).
102
*/
103
truncateFromTurn(turnId: string): Promise<void>;
104
105
/**
106
* Deletes all turns inserted after the given turn (but keeps the
107
* given turn itself). Associated file edits cascade-delete.
108
*/
109
deleteTurnsAfter(turnId: string): Promise<void>;
110
111
/**
112
* Deletes all turns and their associated file edits.
113
*/
114
deleteAllTurns(): Promise<void>;
115
116
/**
117
* Store a file-edit snapshot (metadata + content) for a tool invocation
118
* within a turn.
119
*
120
* If a record for the same `toolCallId` and `filePath` already exists
121
* it is replaced.
122
*/
123
storeFileEdit(edit: IFileEditRecord & IFileEditContent): Promise<void>;
124
125
/**
126
* Retrieve file-edit metadata for the given tool call IDs.
127
* Content blobs are **not** included — use {@link readFileEditContent}
128
* to fetch them on demand. Results are returned in insertion order.
129
*/
130
getFileEdits(toolCallIds: string[]): Promise<IFileEditRecord[]>;
131
132
/**
133
* Retrieve file-edit metadata for all edits in this session.
134
* Content blobs are **not** included — use {@link readFileEditContent}
135
* to fetch them on demand. Results are returned in insertion order.
136
*/
137
getAllFileEdits(): Promise<IFileEditRecord[]>;
138
139
/**
140
* Retrieve file-edit metadata for all edits belonging to a specific turn.
141
* Content blobs are **not** included — use {@link readFileEditContent}
142
* to fetch them on demand. Results are returned in insertion order.
143
*/
144
getFileEditsByTurn(turnId: string): Promise<IFileEditRecord[]>;
145
146
/**
147
* Read the before/after content blobs for a single file edit.
148
* Returns `undefined` if no edit exists for the given key.
149
*/
150
readFileEditContent(toolCallId: string, filePath: string): Promise<IFileEditContent | undefined>;
151
152
// ---- Session metadata ------------------------------------------------
153
154
/**
155
* Read a metadata value by key.
156
* Returns `undefined` if no value has been stored for the key.
157
*/
158
getMetadata(key: string): Promise<string | undefined>;
159
160
/**
161
* Gets a bulk of metadata. For example `getMetadataObject({ foo: true }) -> { foo: 'data' }`
162
*/
163
getMetadataObject<T extends Record<string, unknown>>(obj: T): Promise<{ [K in keyof T]: string | undefined }>;
164
165
/**
166
* Store a metadata key-value pair. Overwrites any existing value for the key.
167
*/
168
setMetadata(key: string, value: string): Promise<void>;
169
170
/**
171
* Bulk-remaps turn IDs using the provided old→new mapping.
172
* Used after copying a database file for a forked session.
173
*/
174
remapTurnIds(mapping: ReadonlyMap<string, string>): Promise<void>;
175
176
/**
177
* Creates a safe, consistent copy of the database at the given path
178
* using SQLite's `VACUUM INTO` command.
179
*/
180
vacuumInto(targetPath: string): Promise<void>;
181
182
/**
183
* Resolves once all in-flight write operations on this database have
184
* settled. Used by graceful shutdown to flush fire-and-forget writes
185
* before the process exits.
186
*/
187
whenIdle(): Promise<void>;
188
189
/**
190
* Close the database connection. After calling this method, the object is
191
* considered disposed and all other methods will reject with an error.
192
*/
193
close(): Promise<void>;
194
}
195
196
/**
197
* Provides persistent, per-session data directories on disk.
198
*
199
* Each session gets a directory under `{userDataPath}/agentSessionData/{sessionId}/`
200
* where internal agent-host code can store arbitrary files (e.g. file snapshots).
201
*
202
* Directories are created lazily — callers should use {@link IFileService.createFolder}
203
* before writing files. Cleanup happens eagerly on session removal and via startup
204
* garbage collection for orphaned directories.
205
*/
206
export interface ISessionDataService {
207
readonly _serviceBrand: undefined;
208
209
/**
210
* Returns the root data directory URI for a session.
211
* Does **not** create the directory on disk; callers use
212
* `IFileService.createFolder()` as needed.
213
*/
214
getSessionDataDir(session: URI): URI;
215
216
/**
217
* Returns the root data directory URI for a session given its raw ID.
218
* Equivalent to {@link getSessionDataDir} but without requiring a full URI.
219
*/
220
getSessionDataDirById(sessionId: string): URI;
221
222
/**
223
* Opens (or creates) a per-session SQLite database. The database file is
224
* stored at `{sessionDataDir}/session.db`. Migrations are applied
225
* automatically on first use.
226
*
227
* Returns a ref-counted reference. Multiple callers for the same session
228
* share the same underlying connection. The connection is closed when
229
* the last reference is disposed.
230
*/
231
openDatabase(session: URI): IReference<ISessionDatabase>;
232
233
/**
234
* Opens an existing per-session database **only if the database file
235
* already exists on disk**. Returns `undefined` when no database has
236
* been created yet, avoiding the side effect of materializing empty
237
* database files during read-only operations like listing sessions.
238
*/
239
tryOpenDatabase(session: URI): Promise<IReference<ISessionDatabase> | undefined>;
240
241
/**
242
* Recursively deletes the data directory for a session, if it exists.
243
*/
244
deleteSessionData(session: URI): Promise<void>;
245
246
/**
247
* Deletes data directories that do not correspond to any known session.
248
* Called at startup; safe to call multiple times.
249
*/
250
cleanupOrphanedData(knownSessionIds: Set<string>): Promise<void>;
251
252
/**
253
* Resolves once all in-flight write operations across every currently
254
* open per-session database have settled. Intended for graceful
255
* shutdown — fire-and-forget writes (e.g. metadata persistence) would
256
* otherwise be lost when the process exits.
257
*/
258
whenIdle(): Promise<void>;
259
}
260
261