fix(plan): clean up session directories and plans on deletion (#20914)

This commit is contained in:
Jerop Kipruto
2026-03-03 09:11:25 -05:00
committed by GitHub
parent 1e2afbb514
commit fca29b0bd8
6 changed files with 105 additions and 15 deletions

View File

@@ -919,6 +919,32 @@ describe('Session Cleanup', () => {
),
);
});
it('should delete the session-specific directory', async () => {
const config = createMockConfig();
const settings: Settings = {
general: {
sessionRetention: {
enabled: true,
maxAge: '1d', // Very short retention to trigger deletion of all but current
},
},
};
// Mock successful file operations
mockFs.access.mockResolvedValue(undefined);
mockFs.unlink.mockResolvedValue(undefined);
mockFs.rm.mockResolvedValue(undefined);
await cleanupExpiredSessions(config, settings);
// Verify that fs.rm was called with the session directory for the deleted session that has sessionInfo
// recent456 should be deleted and its directory removed
expect(mockFs.rm).toHaveBeenCalledWith(
path.join('/tmp/test-project', 'recent456'),
expect.objectContaining({ recursive: true, force: true }),
);
});
});
describe('parseRetentionPeriod format validation', () => {

View File

@@ -115,6 +115,17 @@ export async function cleanupExpiredSessions(
} catch {
/* ignore if doesn't exist */
}
// ALSO cleanup the session-specific directory (contains plans, tasks, etc.)
const sessionDir = path.join(
config.storage.getProjectTempDir(),
sessionId,
);
try {
await fs.rm(sessionDir, { recursive: true, force: true });
} catch {
/* ignore if doesn't exist */
}
}
if (config.getDebugMode()) {

View File

@@ -309,23 +309,33 @@ describe('ChatRecordingService', () => {
});
describe('deleteSession', () => {
it('should delete the session file and tool outputs if they exist', () => {
it('should delete the session file, tool outputs, session directory, and logs if they exist', () => {
const sessionId = 'test-session-id';
const chatsDir = path.join(testTempDir, 'chats');
const logsDir = path.join(testTempDir, 'logs');
const toolOutputsDir = path.join(testTempDir, 'tool-outputs');
const sessionDir = path.join(testTempDir, sessionId);
fs.mkdirSync(chatsDir, { recursive: true });
const sessionFile = path.join(chatsDir, 'test-session-id.json');
fs.mkdirSync(logsDir, { recursive: true });
fs.mkdirSync(toolOutputsDir, { recursive: true });
fs.mkdirSync(sessionDir, { recursive: true });
const sessionFile = path.join(chatsDir, `${sessionId}.json`);
fs.writeFileSync(sessionFile, '{}');
const toolOutputDir = path.join(
testTempDir,
'tool-outputs',
'session-test-session-id',
);
const logFile = path.join(logsDir, `session-${sessionId}.jsonl`);
fs.writeFileSync(logFile, '{}');
const toolOutputDir = path.join(toolOutputsDir, `session-${sessionId}`);
fs.mkdirSync(toolOutputDir, { recursive: true });
chatRecordingService.deleteSession('test-session-id');
chatRecordingService.deleteSession(sessionId);
expect(fs.existsSync(sessionFile)).toBe(false);
expect(fs.existsSync(logFile)).toBe(false);
expect(fs.existsSync(toolOutputDir)).toBe(false);
expect(fs.existsSync(sessionDir)).toBe(false);
});
it('should not throw if session file does not exist', () => {

View File

@@ -569,6 +569,13 @@ export class ChatRecordingService {
fs.unlinkSync(sessionPath);
}
// Cleanup Activity logs in the project logs directory
const logsDir = path.join(tempDir, 'logs');
const logPath = path.join(logsDir, `session-${sessionId}.jsonl`);
if (fs.existsSync(logPath)) {
fs.unlinkSync(logPath);
}
// Cleanup tool outputs for this session
const safeSessionId = sanitizeFilenamePart(sessionId);
const toolOutputDir = path.join(
@@ -585,6 +592,13 @@ export class ChatRecordingService {
) {
fs.rmSync(toolOutputDir, { recursive: true, force: true });
}
// ALSO cleanup the session-specific directory (contains plans, tasks, etc.)
const sessionDir = path.join(tempDir, safeSessionId);
// Robustness: Ensure the path is strictly within the temp root
if (fs.existsSync(sessionDir) && sessionDir.startsWith(tempDir)) {
fs.rmSync(sessionDir, { recursive: true, force: true });
}
} catch (error) {
debugLogger.error('Error deleting session file.', error);
throw error;