diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index f9b92857ff..50811d0527 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -190,8 +190,7 @@ export const globalSettingsSchema = z.object({ enhancementApiConfigId: z.string().optional(), includeTaskHistoryInEnhance: z.boolean().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(), + taskHistoryRetention: z.enum(TASK_HISTORY_RETENTION_OPTIONS).optional(), // Calculated task history storage size info for the Settings > About page taskHistorySize: z .object({ diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index f02ee8309a..18d7c512c0 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -105,7 +105,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return openClineInNewTab({ context, outputChannel }) }, openInNewTab: () => openClineInNewTab({ context, outputChannel }), - settingsButtonClicked: () => { + settingsButtonClicked: (section?: string) => { const visibleProvider = getVisibleProviderOrLog(outputChannel) if (!visibleProvider) { @@ -114,7 +114,11 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt TelemetryService.instance.captureTitleButtonClicked("settings") - visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) + visibleProvider.postMessageToWebview({ + type: "action", + action: "settingsButtonClicked", + values: section ? { section } : undefined, + }) // Also explicitly post the visibility message to trigger scroll reliably visibleProvider.postMessageToWebview({ type: "action", action: "didBecomeVisible" }) }, diff --git a/src/extension.ts b/src/extension.ts index 11e3e702e7..b46145e578 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,6 +45,7 @@ import { import { initializeI18n } from "./i18n" import { flushModels, initializeModelCacheRefresh, refreshModels } from "./api/providers/fetchers/modelCache" import { startBackgroundRetentionPurge } from "./utils/task-history-retention" +import { TASK_HISTORY_RETENTION_OPTIONS, type TaskHistoryRetentionSetting } from "@roo-code/types" /** * Built using https://github.com/microsoft/vscode-webview-ui-toolkit @@ -392,14 +393,11 @@ export async function activate(context: vscode.ExtensionContext) { // By this point, provider is fully initialized and ready to handle deletions { const retentionValue = contextProxy.getValue("taskHistoryRetention") - const retention = - retentionValue === "90" || - retentionValue === "60" || - retentionValue === "30" || - retentionValue === "7" || - retentionValue === "3" - ? retentionValue - : "never" + const retention: TaskHistoryRetentionSetting = TASK_HISTORY_RETENTION_OPTIONS.includes( + retentionValue as TaskHistoryRetentionSetting, + ) + ? (retentionValue as TaskHistoryRetentionSetting) + : "never" startBackgroundRetentionPurge({ globalStoragePath: contextProxy.globalStorageUri.fsPath, log: (m) => outputChannel.appendLine(m), diff --git a/src/utils/task-history-retention.ts b/src/utils/task-history-retention.ts index 567b819e6b..d59b979bd0 100644 --- a/src/utils/task-history-retention.ts +++ b/src/utils/task-history-retention.ts @@ -3,21 +3,13 @@ import * as path from "path" import * as fs from "fs/promises" import type { Dirent } from "fs" +import { TASK_HISTORY_RETENTION_OPTIONS, type TaskHistoryRetentionSetting } from "@roo-code/types" + import { getStorageBasePath } from "./storage" import { GlobalFileNames } from "../shared/globalFileNames" import { t } from "../i18n" -/** - * Allowed retention day values (as numbers). - */ -export type RetentionDays = 90 | 60 | 30 | 7 | 3 - -/** - * Supported values for the retention setting. - * - "never" or 0 disables purging - * - "90" | "60" | "30" | "7" | "3" (string) or 90 | 60 | 30 | 7 | 3 (number) specify days - */ -export type RetentionSetting = "never" | "0" | `${RetentionDays}` | RetentionDays | 0 +export type RetentionSetting = TaskHistoryRetentionSetting export type PurgeResult = { purgedCount: number @@ -280,7 +272,7 @@ export async function purgeOldTasks( */ function normalizeDays(value: RetentionSetting): number { if (value === "never") return 0 - const n = typeof value === "number" ? value : parseInt(String(value), 10) + const n = parseInt(value, 10) return Number.isFinite(n) && n > 0 ? Math.trunc(n) : 0 } @@ -314,11 +306,16 @@ export function startBackgroundRetentionPurge(options: BackgroundPurgeOptions): void (async () => { try { // Skip if retention is disabled - if (retention === "never" || retention === "0" || retention === 0) { + if (retention === "never") { log("[Retention] Background purge skipped: retention is set to 'never'") return } + if (!TASK_HISTORY_RETENTION_OPTIONS.includes(retention)) { + log(`[Retention] Background purge skipped: invalid retention value '${retention}'`) + return + } + log(`[Retention] Starting background purge: setting=${retention}`) const result = await purgeOldTasks(retention, globalStoragePath, log, false, deleteTaskById) @@ -340,7 +337,7 @@ export function startBackgroundRetentionPurge(options: BackgroundPurgeOptions): vscode.window.showInformationMessage(message, viewSettingsLabel, dismissLabel).then((action) => { if (action === viewSettingsLabel) { // Navigate to Roo Code settings About tab - vscode.commands.executeCommand("roo-cline.settingsButtonClicked") + vscode.commands.executeCommand("roo-cline.settingsButtonClicked", "about") } }) } diff --git a/webview-ui/src/components/settings/About.tsx b/webview-ui/src/components/settings/About.tsx index 4899f43b3f..e51df56ef5 100644 --- a/webview-ui/src/components/settings/About.tsx +++ b/webview-ui/src/components/settings/About.tsx @@ -1,4 +1,4 @@ -import { HTMLAttributes, useState, useCallback, useEffect } from "react" +import { HTMLAttributes, useState, useCallback, useEffect, useRef } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { Trans } from "react-i18next" import { @@ -58,6 +58,7 @@ export const About = ({ const { t } = useAppTranslation() const [isRefreshing, setIsRefreshing] = useState(false) const [cachedSize, setCachedSize] = useState(taskHistorySize) + const didRequestInitialSize = useRef(false) // Update cached size when taskHistorySize changes and reset refreshing state useEffect(() => { @@ -67,6 +68,13 @@ export const About = ({ } }, [taskHistorySize]) + // Trigger initial task history size calculation when this tab mounts + useEffect(() => { + if (didRequestInitialSize.current) return + didRequestInitialSize.current = true + vscode.postMessage({ type: "refreshTaskHistorySize" }) + }, []) + const handleRefreshStorageSize = useCallback(() => { setIsRefreshing(true) vscode.postMessage({ type: "refreshTaskHistorySize" }) diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 3a54a82ab1..9720119c8a 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -429,7 +429,7 @@ const SettingsView = forwardRef(({ onDone, t includeCurrentTime: includeCurrentTime ?? true, includeCurrentCost: includeCurrentCost ?? true, maxGitStatusFiles: maxGitStatusFiles ?? 0, -taskHistoryRetention: normalizedTaskHistoryRetention, + taskHistoryRetention: normalizedTaskHistoryRetention, profileThresholds, imageGenerationProvider, openRouterImageApiKey, @@ -566,13 +566,6 @@ taskHistoryRetention: normalizedTaskHistoryRetention, scrollToActiveTab() }, [activeTab, scrollToActiveTab]) - // Effect to trigger task history size calculation when About tab is opened - useEffect(() => { - if (activeTab === "about") { - vscode.postMessage({ type: "refreshTaskHistorySize" }) - } - }, [activeTab]) - // Effect to scroll when the webview becomes visible useLayoutEffect(() => { const handleMessage = (event: MessageEvent) => {