Skip to content

Commit 2238cf5

Browse files
authored
feat: enhance Copilot CLI session management with system message support (#4891)
1 parent 1275fec commit 2238cf5

21 files changed

Lines changed: 158 additions & 508 deletions

src/extension/chatSessions/claude/node/sessionParser/test/claudeCodeSessionService.spec.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ class MockFolderRepositoryManager implements IFolderRepositoryManager {
7878
async initializeFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }
7979
async getRepositoryInfo(): Promise<{ repository: undefined; headBranchName: undefined }> { return { repository: undefined, headBranchName: undefined }; }
8080
async getFolderMRU(): Promise<FolderRepositoryMRUEntry[]> { return this._mruEntries; }
81-
async deleteMRUEntry(): Promise<void> { }
8281
}
8382

8483
// #endregion
@@ -476,7 +475,7 @@ describe('ClaudeCodeSessionService', () => {
476475
noWorkspaceTestingServiceCollection.define(IFolderRepositoryManager, noWorkspaceFolderManager);
477476

478477
noWorkspaceFolderManager.setMRUEntries([
479-
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false },
478+
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() },
480479
]);
481480

482481
const accessor = noWorkspaceTestingServiceCollection.createTestingAccessor();
@@ -515,8 +514,8 @@ describe('ClaudeCodeSessionService', () => {
515514
const mruFolder2 = URI.file('/another/project');
516515

517516
noWorkspaceFolderManager.setMRUEntries([
518-
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false },
519-
{ folder: mruFolder2, repository: undefined, lastAccessed: Date.now() - 1000, isUntitledSessionSelection: false },
517+
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() },
518+
{ folder: mruFolder2, repository: undefined, lastAccessed: Date.now() - 1000 },
520519
]);
521520

522521
const multiMruServiceCollection = store.add(createExtensionUnitTestingServices(store));

src/extension/chatSessions/claude/node/test/claudeProjectFolders.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class MockFolderRepositoryManager implements IFolderRepositoryManager {
2525
async initializeFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }
2626
async getRepositoryInfo(): Promise<any> { return undefined; }
2727
async getFolderMRU(): Promise<FolderRepositoryMRUEntry[]> { return this._mruEntries; }
28-
async deleteMRUEntry(): Promise<void> { }
2928
}
3029

3130
// #endregion
@@ -90,7 +89,7 @@ describe('getProjectFolders', () => {
9089
const mruFolder = URI.file('/Users/test/recent-project');
9190
const workspace = new TestWorkspaceService([]);
9291
const folderManager = new MockFolderRepositoryManager();
93-
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false }]);
92+
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);
9493

9594
const result = await getProjectFolders(workspace, folderManager);
9695

@@ -113,7 +112,7 @@ describe('getProjectFolders', () => {
113112
const mruFolder = URI.file('/Users/test/mru-folder');
114113
const workspace = new TestWorkspaceService([workspaceFolder]);
115114
const folderManager = new MockFolderRepositoryManager();
116-
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false }]);
115+
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);
117116

118117
const result = await getProjectFolders(workspace, folderManager);
119118

src/extension/chatSessions/common/chatSessionMetadataStore.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ export interface IChatSessionMetadataStore {
8080
getWorktreeProperties(folder: Uri): Promise<ChatSessionWorktreeProperties | undefined>;
8181
getSessionWorkspaceFolder(sessionId: string): Promise<vscode.Uri | undefined>;
8282
getSessionWorkspaceFolderEntry(sessionId: string): Promise<WorkspaceFolderEntry | undefined>;
83-
getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]>;
8483
getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]>;
8584
setAdditionalWorkspaces(sessionId: string, workspaces: IWorkspaceInfo[]): Promise<void>;
8685
getSessionFirstUserMessage(sessionId: string): Promise<string | undefined>;

src/extension/chatSessions/common/chatSessionWorkspaceFolderService.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ export const IChatSessionWorkspaceFolderService = createServiceIdentifier<IChatS
1717
*/
1818
export interface IChatSessionWorkspaceFolderService {
1919
readonly _serviceBrand: undefined;
20-
getRecentFolders(): Promise<{ folder: vscode.Uri; lastAccessTime: number }[]>;
21-
/**
22-
* Delete a recent folder from all tracked sessions.
23-
*/
24-
deleteRecentFolder(folder: vscode.Uri): Promise<void>;
2520
deleteTrackedWorkspaceFolder(sessionId: string): Promise<void>;
2621
/**
2722
* Track workspace folder selection for a session (for folders without git repos in multi-root workspaces)

src/extension/chatSessions/common/folderRepositoryManager.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ export interface FolderRepositoryMRUEntry {
7878
* Timestamp of last access (milliseconds since epoch).
7979
*/
8080
readonly lastAccessed: number;
81-
82-
/**
83-
* Whether this entry was used in an untitled session.
84-
*/
85-
readonly isUntitledSessionSelection: boolean;
8681
}
8782

8883
export const IFolderRepositoryManager = createServiceIdentifier<IFolderRepositoryManager>('IFolderRepositoryManager');
@@ -152,6 +147,7 @@ export interface IFolderRepositoryManager {
152147
): Promise<{ repository: vscode.Uri | undefined; headBranchName: string | undefined }>;
153148

154149
/**
150+
* @deprecated
155151
* Get list of most recently used folders and repositories.
156152
*
157153
* This is used for empty workspaces to show a list of previously used
@@ -161,9 +157,4 @@ export interface IFolderRepositoryManager {
161157
* limited to 10 items, with non-existent paths filtered out
162158
*/
163159
getFolderMRU(): Promise<FolderRepositoryMRUEntry[]>;
164-
165-
/**
166-
* Delete an entry from the MRU list.
167-
*/
168-
deleteMRUEntry(folder: vscode.Uri): Promise<void>;
169160
}

src/extension/chatSessions/common/test/mockChatSessionMetadataStore.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ export class MockChatSessionMetadataStore implements IChatSessionMetadataStore {
6161
return undefined;
6262
}
6363

64-
async getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]> {
65-
return Array.from(this._workspaceFolders.values());
66-
}
67-
6864
async getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]> {
6965
return this._additionalWorkspaces.get(sessionId) ?? [];
7066
}

src/extension/chatSessions/copilotcli/node/copilotcliSessionService.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export interface ICopilotCLISessionItem {
6262
readonly status?: ChatSessionStatus;
6363
readonly workingDirectory?: Uri;
6464
}
65-
6665
export type ExtendedChatRequest = ChatRequest & { prompt: string };
6766
export type ISessionOptions = {
6867
model?: string;

src/extension/chatSessions/copilotcli/node/test/copilotCliSessionService.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ class NullAgentSessionsWorkspace implements IAgentSessionsWorkspace {
6262
}
6363

6464
class NullChatSessionWorkspaceFolderService extends mock<IChatSessionWorkspaceFolderService>() {
65-
override getRecentFolders = vi.fn(async () => []);
66-
override deleteRecentFolder = vi.fn(async () => { });
6765
override deleteTrackedWorkspaceFolder = vi.fn(async () => { });
6866
override trackSessionWorkspaceFolder = vi.fn(async () => { });
6967
override getSessionWorkspaceFolder = vi.fn(async () => undefined);

src/extension/chatSessions/vscode-node/chatSessionMetadataStoreImpl.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { findLast } from '../../../util/vs/base/common/arraysFind';
1212
import { SequencerByKey, ThrottledDelayer } from '../../../util/vs/base/common/async';
1313
import { Lazy } from '../../../util/vs/base/common/lazy';
1414
import { Disposable } from '../../../util/vs/base/common/lifecycle';
15-
import { ResourceMap } from '../../../util/vs/base/common/map';
1615
import { dirname, isEqual } from '../../../util/vs/base/common/resources';
1716
import { ChatSessionMetadataFile, IChatSessionMetadataStore, RequestDetails, WorkspaceFolderEntry } from '../common/chatSessionMetadataStore';
1817
import { ChatSessionWorktreeData, ChatSessionWorktreeProperties } from '../common/chatSessionWorktreeService';
@@ -245,18 +244,6 @@ export class ChatSessionMetadataStore extends Disposable implements IChatSession
245244
return metadata.workspaceFolder;
246245
}
247246

248-
async getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]> {
249-
await this._intialize.value;
250-
const entries = new ResourceMap<number>();
251-
for (const metadata of Object.values(this._cache)) {
252-
if (metadata.workspaceFolder?.folderPath) {
253-
const folderUri = Uri.file(metadata.workspaceFolder.folderPath);
254-
entries.set(folderUri, Math.max(entries.get(folderUri) ?? 0, metadata.workspaceFolder.timestamp));
255-
}
256-
}
257-
return Array.from(entries.entries()).map(([folderUri, timestamp]) => ({ folderPath: folderUri.fsPath, timestamp }));
258-
}
259-
260247
async getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]> {
261248
const metadata = await this.getSessionMetadata(sessionId);
262249
if (!metadata?.additionalWorkspaces?.length) {

src/extension/chatSessions/vscode-node/chatSessionWorkspaceFolderServiceImpl.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ import { IGitService } from '../../../platform/git/common/gitService';
1010
import { parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
1111
import { DiffChange } from '../../../platform/git/vscode/git';
1212
import { ILogService } from '../../../platform/log/common/logService';
13-
import { coalesce } from '../../../util/vs/base/common/arrays';
1413
import { SequencerByKey } from '../../../util/vs/base/common/async';
1514
import { Disposable } from '../../../util/vs/base/common/lifecycle';
16-
import { ResourceMap, ResourceSet } from '../../../util/vs/base/common/map';
15+
import { ResourceMap } from '../../../util/vs/base/common/map';
1716
import * as path from '../../../util/vs/base/common/path';
18-
import { isEqual } from '../../../util/vs/base/common/resources';
1917
import { generateUuid } from '../../../util/vs/base/common/uuid';
2018
import { IChatSessionMetadataStore, WorkspaceFolderEntry } from '../common/chatSessionMetadataStore';
2119
import { IChatSessionWorkspaceFolderService } from '../common/chatSessionWorkspaceFolderService';
@@ -32,8 +30,6 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh
3230

3331
private readonly workspaceFolderChanges = new ResourceMap<ChatSessionWorktreeFile[]>();
3432
private readonly workspaceState = new Map<string, WorkspaceFolderEntry>();
35-
private recentFolders: { folder: vscode.Uri; lastAccessTime: number }[] = [];
36-
private readonly deletedFolders = new ResourceSet();
3733
private readonly workspaceChangesSequencer = new SequencerByKey<string>();
3834

3935
constructor(
@@ -45,28 +41,6 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh
4541
super();
4642
}
4743

48-
public async deleteRecentFolder(folder: vscode.Uri): Promise<void> {
49-
this.recentFolders = this.recentFolders.filter(entry => !isEqual(entry.folder, folder));
50-
this.deletedFolders.add(folder);
51-
}
52-
53-
public async getRecentFolders(): Promise<{ folder: vscode.Uri; lastAccessTime: number }[]> {
54-
const items = await this.metadataStore.getUsedWorkspaceFolders();
55-
this.recentFolders = coalesce(items.map(item => {
56-
if (!item.folderPath) {
57-
return;
58-
}
59-
const folder = vscode.Uri.file(item.folderPath);
60-
if (this.deletedFolders.has(folder)) {
61-
return;
62-
}
63-
return {
64-
folder,
65-
lastAccessTime: item.timestamp
66-
};
67-
})).sort((a, b) => b.lastAccessTime - a.lastAccessTime);
68-
return this.recentFolders;
69-
}
7044
async deleteTrackedWorkspaceFolder(sessionId: string): Promise<void> {
7145
this.workspaceState.delete(sessionId);
7246
await this.metadataStore.deleteSessionMetadata(sessionId);

0 commit comments

Comments
 (0)