36
return db;
37
}
39
>
protected destroyReferencedObject(_key: string, object: ISessionDatabase): void {
40
this.liveDatabases.delete(object);
41
object.dispose();
42
}
44
>
45
>
/**
46
>
* Implementation of {@link ISessionDataService} that stores per-session data
47
>
* under `{userDataPath}/agentSessionData/{sessionId}/`.
48
>
*/
49
>
export class SessionDataService implements ISessionDataService {
50
>
declare readonly _serviceBrand: undefined;
51
>
52
>
private readonly _basePath: URI;
53
>
private readonly _databases: SessionDatabaseCollection;
54
>
private readonly _onWillDeleteSessionData = new Emitter<IWillDeleteSessionDataEvent>();
55
>
56
>
get onWillDeleteSessionData(): Event<IWillDeleteSessionDataEvent> {
57
>
return this._onWillDeleteSessionData.event;
58
>
}
59
>
60
>
constructor(
61
>
userDataPath: URI,
62
>
@IFileService private readonly _fileService: IFileService,
63
>
@ILogService private readonly _logService: ILogService,
64
>
getDbPath?: (key: string) => string, // for testing
65
>
) {
66
>
this._basePath = URI.joinPath(userDataPath, 'agentSessionData');
67
>
this._databases = new SessionDatabaseCollection(
68
>
getDbPath ?? (key => URI.joinPath(this._basePath, key, SESSION_DB_FILENAME).fsPath),
69
>
this._logService,
70
>
);
71
>
}
72
>
73
>
getSessionDataDir(session: URI): URI {
74
return URI.joinPath(this._basePath, this._sanitizedSessionKey(session));
75
}
77
>
getSessionDataDirById(sessionId: string): URI {
78
const sanitized = sessionId.replace(/[^a-zA-Z0-9_.-]/g, '-');
79
return URI.joinPath(this._basePath, sanitized);
80
}
82
>
private _sanitizedSessionKey(session: URI): string {
83
return this._dataKey(session).replace(/[^a-zA-Z0-9_.-]/g, '-');
84
}
86
>
/**
87
>
* Derives the per-URI storage key. Chat channel URIs
88
>
* (`ahp-chat://<chatId>/<base64(session)>`) carry the chat id in the
89
>
* authority while encoding the SAME owning-session URI in the path, so
90
>
* keying only by the path (via {@link AgentSession.id}) would collapse
91
>
* every peer chat of a session onto one data directory and database.
92
>
* Prefixing with the authority gives each chat its own storage while
93
>
* leaving plain session URIs (no authority) unchanged.
94
>
*/
95
>
private _dataKey(uri: URI): string {
96
const id = AgentSession.id(uri);
97
return uri.authority ? `${uri.authority}-${id}` : id;
98
}
100
>
openDatabase(session: URI): IReference<ISessionDatabase> {
101
return this._databases.acquire(this._sanitizedSessionKey(session));
102
}
104
>
async tryOpenDatabase(session: URI): Promise<IReference<ISessionDatabase> | undefined> {
105
const key = this._sanitizedSessionKey(session);
106
const dbPath = URI.joinPath(this._basePath, key, SESSION_DB_FILENAME);