This commit is contained in:
Supun Geethanjana Jayasinghe 2026-04-21 11:34:46 +00:00 committed by GitHub
commit 723bfc9ce4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 21 deletions

View file

@ -24,7 +24,9 @@ vi.mock('@google/gemini-cli-core', async () => {
const actual = await vi.importActual('@google/gemini-cli-core');
return {
...actual,
ChatRecordingService: vi.fn(),
ChatRecordingService: Object.assign(vi.fn(), {
deleteSessionFiles: vi.fn(),
}),
generateSummary: vi.fn().mockResolvedValue(undefined),
writeToStdout: mocks.writeToStdout,
writeToStderr: mocks.writeToStderr,
@ -357,13 +359,8 @@ describe('deleteSession', () => {
}) as unknown as InstanceType<typeof SessionSelector>,
);
// Mock ChatRecordingService
vi.mocked(ChatRecordingService).mockImplementation(
() =>
({
deleteSession: mockDeleteSession,
}) as unknown as InstanceType<typeof ChatRecordingService>,
);
// Mock ChatRecordingService.deleteSessionFiles
ChatRecordingService.deleteSessionFiles = mockDeleteSession;
});
afterEach(() => {
@ -411,7 +408,10 @@ describe('deleteSession', () => {
// Assert
expect(mockListSessions).toHaveBeenCalledOnce();
expect(mockDeleteSession).toHaveBeenCalledWith('session-file-123');
expect(mockDeleteSession).toHaveBeenCalledWith(
mockConfig,
'session-file-123',
);
expect(mocks.writeToStdout).toHaveBeenCalledWith(
'Deleted session 1: Test session (some time ago)',
);
@ -458,7 +458,10 @@ describe('deleteSession', () => {
// Assert
expect(mockListSessions).toHaveBeenCalledOnce();
expect(mockDeleteSession).toHaveBeenCalledWith('session-file-2');
expect(mockDeleteSession).toHaveBeenCalledWith(
mockConfig,
'session-file-2',
);
expect(mocks.writeToStdout).toHaveBeenCalledWith(
'Deleted session 2: Second session (some time ago)',
);
@ -641,7 +644,10 @@ describe('deleteSession', () => {
await deleteSession(mockConfig, '1');
// Assert
expect(mockDeleteSession).toHaveBeenCalledWith('session-file-1');
expect(mockDeleteSession).toHaveBeenCalledWith(
mockConfig,
'session-file-1',
);
expect(mocks.writeToStderr).toHaveBeenCalledWith(
'Failed to delete session: File deletion failed',
);
@ -732,7 +738,10 @@ describe('deleteSession', () => {
await deleteSession(mockConfig, '1');
// Assert
expect(mockDeleteSession).toHaveBeenCalledWith('session-file-1');
expect(mockDeleteSession).toHaveBeenCalledWith(
mockConfig,
'session-file-1',
);
expect(mocks.writeToStdout).toHaveBeenCalledWith(
expect.stringContaining('Oldest session'),
);

View file

@ -96,8 +96,7 @@ export async function deleteSession(
try {
// Use ChatRecordingService to delete the session
const chatRecordingService = new ChatRecordingService(config);
await chatRecordingService.deleteSession(sessionToDelete.file);
await ChatRecordingService.deleteSessionFiles(config, sessionToDelete.file);
const time = formatRelativeTime(sessionToDelete.lastUpdated);
writeToStdout(

View file

@ -24,6 +24,7 @@ import type {
} from '@google/genai';
import { debugLogger } from '../utils/debugLogger.js';
import type { AgentLoopContext } from '../config/agent-loop-context.js';
import type { Config } from '../config/config.js';
import {
SESSION_FILE_PREFIX,
type TokensSummary,
@ -620,22 +621,40 @@ export class ChatRecordingService {
* @throws {Error} If shortId validation fails.
*/
async deleteSession(sessionIdOrBasename: string): Promise<void> {
return ChatRecordingService.deleteSessionFiles(
this.context.config,
sessionIdOrBasename,
);
}
/**
* Static version of deleteSession that only requires Config.
* Useful for management operations outside of an active agent loop.
*/
static async deleteSessionFiles(
config: Config,
sessionIdOrBasename: string,
): Promise<void> {
try {
const tempDir = this.context.config.storage.getProjectTempDir();
const tempDir = config.storage.getProjectTempDir();
const chatsDir = path.join(tempDir, 'chats');
const shortId = this.deriveShortId(sessionIdOrBasename);
const shortId = ChatRecordingService.deriveShortId(sessionIdOrBasename);
// Using stat instead of existsSync for async sanity
if (!(await fs.promises.stat(chatsDir).catch(() => null))) {
return; // Nothing to delete
}
const matchingFiles = await this.getMatchingSessionFiles(
const matchingFiles = await ChatRecordingService.getMatchingSessionFiles(
chatsDir,
shortId,
);
for (const file of matchingFiles) {
await this.deleteSessionAndArtifacts(chatsDir, file, tempDir);
await ChatRecordingService.deleteSessionAndArtifacts(
chatsDir,
file,
tempDir,
);
}
} catch (error) {
debugLogger.error('Error deleting session file.', error);
@ -643,7 +662,7 @@ export class ChatRecordingService {
}
}
private deriveShortId(sessionIdOrBasename: string): string {
private static deriveShortId(sessionIdOrBasename: string): string {
let shortId = sessionIdOrBasename;
if (sessionIdOrBasename.startsWith(SESSION_FILE_PREFIX)) {
const withoutExt = sessionIdOrBasename.replace(/\.jsonl?$/, '');
@ -662,7 +681,7 @@ export class ChatRecordingService {
return shortId;
}
private async getMatchingSessionFiles(
private static async getMatchingSessionFiles(
chatsDir: string,
shortId: string,
): Promise<string[]> {
@ -677,7 +696,7 @@ export class ChatRecordingService {
/**
* Deletes a single session file and its associated logs, tool-outputs, and directory.
*/
private async deleteSessionAndArtifacts(
private static async deleteSessionAndArtifacts(
chatsDir: string,
file: string,
tempDir: string,