fix: resolve merge conflict, use startBackgroundRetentionPurge() for cleaner code

- Resolved merge conflict in extension.ts, keeping extracted function approach
- startBackgroundRetentionPurge() encapsulates all purge logic in task-history-retention.ts
- Added getStorageBasePath mock to task-storage-size tests (addresses roomote review)
This commit is contained in:
daniel-lxs 2026-01-19 23:16:57 -05:00 • committed by Hannes Rudolph
parent 33b58beed0
commit 523abdb25d
4 changed files with 89 additions and 39 deletions

View file

@ -159,9 +159,9 @@ vi.mock("../utils/autoImportSettings", () => ({
autoImportSettings: vi.fn().mockResolvedValue(undefined),
}))
// Avoid filesystem access during activation by stubbing purge
// Avoid filesystem access during activation by stubbing background purge
vi.mock("../utils/task-history-retention", () => ({
purgeOldTasks: vi.fn().mockResolvedValue({ purgedCount: 0, cutoff: null }),
startBackgroundRetentionPurge: vi.fn(),
}))
// Ensure storage base path resolves to provided path to avoid touching VS Code config

View file

@ -44,7 +44,7 @@ import {
} from "./activate"
import { initializeI18n } from "./i18n"
import { flushModels, initializeModelCacheRefresh, refreshModels } from "./api/providers/fetchers/modelCache"
import { purgeOldTasks, RetentionSetting } from "./utils/task-history-retention"
import { startBackgroundRetentionPurge } from "./utils/task-history-retention"
/**
* Built using https://github.com/microsoft/vscode-webview-ui-toolkit
@ -389,42 +389,16 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.executeCommand(`${Package.name}.activationCompleted`)
// Task history retention purge (runs in background after activation)
// Fire-and-forget to avoid blocking extension startup
void (async () => {
try {
const config = vscode.workspace.getConfiguration(Package.name)
const retention = config.get<string>("taskHistoryRetention", "never") ?? "never"
// Skip if retention is disabled (default)
if (retention === "never") {
return
}
outputChannel.appendLine(`[Retention] Starting background purge: setting=${retention}`)
const result = await purgeOldTasks(
retention as RetentionSetting,
contextProxy.globalStorageUri.fsPath,
(m) => outputChannel.appendLine(m),
false,
async (taskId: string) => {
// Reuse the same internal deletion logic as the History view so that
// checkpoints, shadow repositories, and task state are cleaned up consistently.
await provider.deleteTaskWithId(taskId)
},
)
if (result.purgedCount > 0) {
outputChannel.appendLine(
`[Retention] Background purge complete: purged=${result.purgedCount}, cutoff=${result.cutoff ? new Date(result.cutoff).toISOString() : "none"}`,
)
}
} catch (error) {
outputChannel.appendLine(
`[Retention] Failed during background purge: ${error instanceof Error ? error.message : String(error)}`,
)
}
})()
// By this point, provider is fully initialized and ready to handle deletions
startBackgroundRetentionPurge({
globalStoragePath: contextProxy.globalStorageUri.fsPath,
log: (m) => outputChannel.appendLine(m),
deleteTaskById: async (taskId: string) => {
// Reuse the same internal deletion logic as the History view so that
// checkpoints, shadow repositories, and task state are cleaned up consistently.
await provider.deleteTaskWithId(taskId)
},
})
// Implements the `RooCodeAPI` interface.
const socketPath = process.env.ROO_CODE_IPC_SOCKET_PATH

View file

@ -1,6 +1,11 @@
import * as path from "path"
import { calculateTaskStorageSize, formatBytes } from "../task-storage-size"
// Mock storage to avoid VS Code config access during tests
vi.mock("../storage", () => ({
getStorageBasePath: (p: string) => Promise.resolve(p),
}))
// Mock fs/promises
const mockReaddir = vi.fn()
const mockStat = vi.fn()

View file

@ -1,9 +1,11 @@
import * as vscode from "vscode"
import * as path from "path"
import * as fs from "fs/promises"
import type { Dirent } from "fs"
import { getStorageBasePath } from "./storage"
import { GlobalFileNames } from "../shared/globalFileNames"
import { Package } from "../shared/package"
/**
* Allowed retention day values (as numbers).
@ -281,3 +283,72 @@ function normalizeDays(value: RetentionSetting): number {
const n = typeof value === "number" ? value : parseInt(String(value), 10)
return Number.isFinite(n) && n > 0 ? Math.trunc(n) : 0
}
/**
* Options for starting the background retention purge.
*/
export interface BackgroundPurgeOptions {
/** VS Code global storage fsPath */
globalStoragePath: string
/** Logger function */
log: (message: string) => void
/** Function to delete a task by ID (should use ClineProvider.deleteTaskWithId for full cleanup) */
deleteTaskById: (taskId: string, taskDirPath: string) => Promise<void>
}
/**
* Starts the task history retention purge in the background.
* This function is designed to be called after extension activation completes,
* using a fire-and-forget pattern (void) to avoid blocking activation.
*
* It reads the retention setting from VS Code configuration, executes the purge,
* and shows a notification if tasks were deleted.
*
* @param options Configuration options for the background purge
*/
export function startBackgroundRetentionPurge(options: BackgroundPurgeOptions): void {
const { globalStoragePath, log, deleteTaskById } = options
void (async () => {
try {
const config = vscode.workspace.getConfiguration(Package.name)
const retention = config.get<string>("taskHistoryRetention", "never") ?? "never"
// Skip if retention is disabled
if (retention === "never") {
log("[Retention] Background purge skipped: retention is set to 'never'")
return
}
log(`[Retention] Starting background purge: setting=${retention}`)
const result = await purgeOldTasks(
retention as RetentionSetting,
globalStoragePath,
log,
false,
deleteTaskById,
)
log(
`[Retention] Background purge complete: purged=${result.purgedCount}, cutoff=${result.cutoff ?? "none"}`,
)
// Show user notification if tasks were deleted
if (result.purgedCount > 0) {
const message = `Roo Code deleted ${result.purgedCount} task${result.purgedCount === 1 ? "" : "s"} older than ${retention} day${retention === "1" ? "" : "s"}`
vscode.window.showInformationMessage(message, "View Settings", "Dismiss").then((action) => {
if (action === "View Settings") {
vscode.commands.executeCommand(
"workbench.action.openSettings",
"roo-cline.taskHistoryRetention",
)
}
})
}
} catch (error) {
log(`[Retention] Failed during background purge: ${error instanceof Error ? error.message : String(error)}`)
}
})()
}