diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 681324fe7a..f9b92857ff 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -44,6 +44,14 @@ export const MAX_CHECKPOINT_TIMEOUT_SECONDS = 60 */ export const DEFAULT_CHECKPOINT_TIMEOUT_SECONDS = 15 +/** + * Allowed values for the task history retention setting. + * Stored as strings in most UI/extension flows. + */ +export const TASK_HISTORY_RETENTION_OPTIONS = ["never", "90", "60", "30", "7", "3"] as const + +export type TaskHistoryRetentionSetting = (typeof TASK_HISTORY_RETENTION_OPTIONS)[number] + /** * GlobalSettings */ @@ -181,8 +189,9 @@ export const globalSettingsSchema = z.object({ customSupportPrompts: customSupportPromptsSchema.optional(), enhancementApiConfigId: z.string().optional(), includeTaskHistoryInEnhance: z.boolean().optional(), - // Auto-delete task history on extension reload. "never" | "90" | "60" | "30" | "7" | "3" - taskHistoryRetention: z.union([z.enum(["never", "90", "60", "30", "7", "3"]), z.number()]).optional(), + // Auto-delete task history on extension reload. + // Note: we accept `number` for backwards compatibility with older persisted state. + taskHistoryRetention: z.union([z.enum(TASK_HISTORY_RETENTION_OPTIONS), z.number()]).optional(), // Calculated task history storage size info for the Settings > About page taskHistorySize: z .object({ diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 24a9b9e417..14a469f5a3 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -597,7 +597,7 @@ export const webviewMessageHandler = async ( .getConfiguration(Package.name) .update("deniedCommands", newValue, vscode.ConfigurationTarget.Global) } else if (key === "taskHistoryRetention") { - // taskHistoryRetention is stored in Roo application state, not VS Code settings + // taskHistoryRetention is stored in Roo application state (global state), not VS Code settings. newValue = ((value ?? "never") as string).toString() } else if (key === "ttsEnabled") { newValue = value ?? true diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 636d26f76c..89043321d6 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -252,5 +252,13 @@ "docsLink": { "label": "Docs", "url": "https://docs.roocode.com" + }, + "taskHistoryRetention": { + "purgeNotification": "Roo Code deleted {{count}} task older than {{days}} days", + "purgeNotification_plural": "Roo Code deleted {{count}} tasks older than {{days}} days", + "actions": { + "viewSettings": "View Settings", + "dismiss": "Dismiss" + } } } diff --git a/src/utils/formatBytes.ts b/src/utils/formatBytes.ts new file mode 100644 index 0000000000..51a621fdd7 --- /dev/null +++ b/src/utils/formatBytes.ts @@ -0,0 +1,18 @@ +/** + * Formats bytes into a human-readable string with appropriate units. + * + * Note: This is intentionally simple (base-2 / 1024) and consistent with existing + * formatting expectations in tests and UI. + */ +export function formatBytes(bytes: number): string { + if (bytes === 0) return "0 B" + + const units = ["B", "KB", "MB", "GB", "TB"] + const k = 1024 + const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), units.length - 1) + const size = bytes / Math.pow(k, i) + + // Use 2 decimal places for MB and above, 0 for B and KB + const decimals = i >= 2 ? 2 : 0 + return `${size.toFixed(decimals)} ${units[i]}` +} diff --git a/src/utils/task-history-retention.ts b/src/utils/task-history-retention.ts index e7a7157f3e..567b819e6b 100644 --- a/src/utils/task-history-retention.ts +++ b/src/utils/task-history-retention.ts @@ -5,6 +5,7 @@ import type { Dirent } from "fs" import { getStorageBasePath } from "./storage" import { GlobalFileNames } from "../shared/globalFileNames" +import { t } from "../i18n" /** * Allowed retention day values (as numbers). @@ -328,10 +329,16 @@ export function startBackgroundRetentionPurge(options: BackgroundPurgeOptions): // 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} days` + const message = t("common:taskHistoryRetention.purgeNotification", { + count: result.purgedCount, + days: retention, + }) - vscode.window.showInformationMessage(message, "View Settings", "Dismiss").then((action) => { - if (action === "View Settings") { + const viewSettingsLabel = t("common:taskHistoryRetention.actions.viewSettings") + const dismissLabel = t("common:taskHistoryRetention.actions.dismiss") + + vscode.window.showInformationMessage(message, viewSettingsLabel, dismissLabel).then((action) => { + if (action === viewSettingsLabel) { // Navigate to Roo Code settings About tab vscode.commands.executeCommand("roo-cline.settingsButtonClicked") } diff --git a/src/utils/task-storage-size.ts b/src/utils/task-storage-size.ts index 669b6d733d..cd16766735 100644 --- a/src/utils/task-storage-size.ts +++ b/src/utils/task-storage-size.ts @@ -3,6 +3,7 @@ import * as fs from "fs/promises" import type { Dirent, Stats } from "fs" import { getStorageBasePath } from "./storage" +import { formatBytes } from "./formatBytes" /** * Result of calculating task history storage size @@ -16,23 +17,8 @@ export interface TaskStorageSizeResult { formattedSize: string } -/** - * Formats bytes into a human-readable string with appropriate units. - * @param bytes Number of bytes - * @returns Formatted string (e.g., "12.34 MB", "5.67 GB") - */ -export function formatBytes(bytes: number): string { - if (bytes === 0) return "0 B" - - const units = ["B", "KB", "MB", "GB", "TB"] - const k = 1024 - const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), units.length - 1) - const size = bytes / Math.pow(k, i) - - // Use 2 decimal places for MB and above, 0 for B and KB - const decimals = i >= 2 ? 2 : 0 - return `${size.toFixed(decimals)} ${units[i]}` -} +// Re-export for backwards compatibility with existing imports/tests. +export { formatBytes } /** * Recursively calculates the total size of a directory. diff --git a/webview-ui/src/components/settings/About.tsx b/webview-ui/src/components/settings/About.tsx index 77be9d22fb..4899f43b3f 100644 --- a/webview-ui/src/components/settings/About.tsx +++ b/webview-ui/src/components/settings/About.tsx @@ -16,7 +16,7 @@ import { } from "lucide-react" import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import type { TelemetrySetting } from "@roo-code/types" +import type { TelemetrySetting, TaskHistoryRetentionSetting } from "@roo-code/types" import { Package } from "@roo/package" @@ -39,8 +39,8 @@ type AboutProps = HTMLAttributes & { setTelemetrySetting: (setting: TelemetrySetting) => void debug?: boolean setDebug?: (debug: boolean) => void - taskHistoryRetention: "never" | "90" | "60" | "30" | "7" | "3" - setTaskHistoryRetention: (value: "never" | "90" | "60" | "30" | "7" | "3") => void + taskHistoryRetention: TaskHistoryRetentionSetting + setTaskHistoryRetention: (value: TaskHistoryRetentionSetting) => void taskHistorySize?: TaskHistorySize } @@ -206,7 +206,7 @@ export const About = ({