fix: address mrubens review feedback for task history retention

This commit is contained in:
Hannes Rudolph 2026-01-19 22:35:17 -07:00
parent 0ba7cdac58
commit bf483c9276
8 changed files with 62 additions and 37 deletions

View file

@ -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({

View file

@ -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

View file

@ -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"
}
}
}

18
src/utils/formatBytes.ts Normal file
View file

@ -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]}`
}

View file

@ -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")
}

View file

@ -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.

View file

@ -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<HTMLDivElement> & {
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 = ({
<div className="mt-2">
<Select
value={taskHistoryRetention}
onValueChange={(value: "never" | "90" | "60" | "30" | "7" | "3") => {
onValueChange={(value: TaskHistoryRetentionSetting) => {
setTaskHistoryRetention(value)
}}>
<SelectTrigger className="w-64">

View file

@ -37,6 +37,8 @@ import {
type TelemetrySetting,
DEFAULT_CHECKPOINT_TIMEOUT_SECONDS,
ImageGenerationProvider,
TASK_HISTORY_RETENTION_OPTIONS,
type TaskHistoryRetentionSetting,
} from "@roo-code/types"
import { vscode } from "@src/utils/vscode"
@ -627,16 +629,11 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
// Determine which tab content to render (for indexing or active display)
const renderTab = isIndexing ? sectionNames[indexingTabIndex] : activeTab
type TaskHistoryRetentionSetting = React.ComponentProps<typeof About>["taskHistoryRetention"]
const normalizedTaskHistoryRetention: TaskHistoryRetentionSetting =
taskHistoryRetention === "never" ||
taskHistoryRetention === "90" ||
taskHistoryRetention === "60" ||
taskHistoryRetention === "30" ||
taskHistoryRetention === "7" ||
taskHistoryRetention === "3"
? taskHistoryRetention
: "never"
const normalizedTaskHistoryRetention: TaskHistoryRetentionSetting = TASK_HISTORY_RETENTION_OPTIONS.includes(
taskHistoryRetention as TaskHistoryRetentionSetting,
)
? (taskHistoryRetention as TaskHistoryRetentionSetting)
: "never"
// Handle search navigation - switch to the correct tab and scroll to the element
const handleSearchNavigate = useCallback(