diff --git a/docs/cli/settings.md b/docs/cli/settings.md index faf3fca3f0..571d90aaf6 100644 --- a/docs/cli/settings.md +++ b/docs/cli/settings.md @@ -32,8 +32,8 @@ they appear in the UI. | Plan Model Routing | `general.plan.modelRouting` | Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pro for the planning phase and Flash for the implementation phase. | `true` | | Max Chat Model Attempts | `general.maxAttempts` | Maximum number of attempts for requests to the main chat model. Cannot exceed 10. | `10` | | Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` | -| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `false` | -| Keep chat history | `general.sessionRetention.maxAge` | Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") | `undefined` | +| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `true` | +| Keep chat history | `general.sessionRetention.maxAge` | Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") | `"30d"` | ### Output diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index a6c9ddccfd..524b00e00f 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -159,12 +159,12 @@ their corresponding top-level category object in your `settings.json` file. - **`general.sessionRetention.enabled`** (boolean): - **Description:** Enable automatic session cleanup - - **Default:** `false` + - **Default:** `true` - **`general.sessionRetention.maxAge`** (string): - **Description:** Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") - - **Default:** `undefined` + - **Default:** `"30d"` - **`general.sessionRetention.maxCount`** (number): - **Description:** Alternative: Maximum number of sessions to keep (most @@ -175,11 +175,6 @@ their corresponding top-level category object in your `settings.json` file. - **Description:** Minimum retention period (safety limit, defaults to "1d") - **Default:** `"1d"` -- **`general.sessionRetention.warningAcknowledged`** (boolean): - - **Description:** INTERNAL: Whether the user has acknowledged the session - retention warning - - **Default:** `false` - #### `output` - **`output.format`** (enum): diff --git a/integration-tests/json-output.test.ts b/integration-tests/json-output.test.ts index 215cf21226..473b966d5a 100644 --- a/integration-tests/json-output.test.ts +++ b/integration-tests/json-output.test.ts @@ -81,7 +81,9 @@ describe('JSON output', () => { const message = (thrown as Error).message; // Use a regex to find the first complete JSON object in the string - const jsonMatch = message.match(/{[\s\S]*}/); + // We expect the JSON to start with a quote (e.g. {"error": ...}) to avoid + // matching random error objects printed to stderr (like ENOENT). + const jsonMatch = message.match(/{\s*"[\s\S]*}/); // Fail if no JSON-like text was found expect( diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index 657968a3b6..4e9faf5767 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -185,9 +185,6 @@ export interface SessionRetentionSettings { /** Minimum retention period (safety limit, defaults to "1d") */ minRetention?: string; - - /** INTERNAL: Whether the user has acknowledged the session retention warning */ - warningAcknowledged?: boolean; } export interface SettingsError { diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 599c8e586b..38b71e433f 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -339,7 +339,7 @@ const SETTINGS_SCHEMA = { label: 'Enable Session Cleanup', category: 'General', requiresRestart: false, - default: false, + default: true as boolean, description: 'Enable automatic session cleanup', showInDialog: true, }, @@ -348,7 +348,7 @@ const SETTINGS_SCHEMA = { label: 'Keep chat history', category: 'General', requiresRestart: false, - default: undefined as string | undefined, + default: '30d' as string, description: 'Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w")', showInDialog: true, @@ -372,16 +372,6 @@ const SETTINGS_SCHEMA = { description: `Minimum retention period (safety limit, defaults to "${DEFAULT_MIN_RETENTION}")`, showInDialog: false, }, - warningAcknowledged: { - type: 'boolean', - label: 'Warning Acknowledged', - category: 'General', - requiresRestart: false, - default: false, - showInDialog: false, - description: - 'INTERNAL: Whether the user has acknowledged the session retention warning', - }, }, description: 'Settings for automatic session cleanup.', }, diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index d42cad8495..4f8d739340 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -146,7 +146,6 @@ import { requestConsentInteractive } from '../config/extensions/consent.js'; import { useSessionBrowser } from './hooks/useSessionBrowser.js'; import { useSessionResume } from './hooks/useSessionResume.js'; import { useIncludeDirsTrust } from './hooks/useIncludeDirsTrust.js'; -import { useSessionRetentionCheck } from './hooks/useSessionRetentionCheck.js'; import { isWorkspaceTrusted } from '../config/trustedFolders.js'; import { useSettings } from './contexts/SettingsContext.js'; import { terminalCapabilityManager } from './utils/terminalCapabilityManager.js'; @@ -1548,28 +1547,6 @@ Logging in with Google... Restarting Gemini CLI to continue. useIncludeDirsTrust(config, isTrustedFolder, historyManager, setCustomDialog); - const handleAutoEnableRetention = useCallback(() => { - const userSettings = settings.forScope(SettingScope.User).settings; - const currentRetention = userSettings.general?.sessionRetention ?? {}; - - settings.setValue(SettingScope.User, 'general.sessionRetention', { - ...currentRetention, - enabled: true, - maxAge: '30d', - warningAcknowledged: true, - }); - }, [settings]); - - const { - shouldShowWarning: shouldShowRetentionWarning, - checkComplete: retentionCheckComplete, - sessionsToDeleteCount, - } = useSessionRetentionCheck( - config, - settings.merged, - handleAutoEnableRetention, - ); - const tabFocusTimeoutRef = useRef(null); useEffect(() => { @@ -2015,7 +1992,7 @@ Logging in with Google... Restarting Gemini CLI to continue. const nightly = props.version.includes('nightly'); const dialogsVisible = - (shouldShowRetentionWarning && retentionCheckComplete) || + shouldShowIdePrompt || shouldShowIdePrompt || isFolderTrustDialogOpen || isPolicyUpdateDialogOpen || @@ -2202,9 +2179,7 @@ Logging in with Google... Restarting Gemini CLI to continue. history: historyManager.history, historyManager, isThemeDialogOpen, - shouldShowRetentionWarning: - shouldShowRetentionWarning && retentionCheckComplete, - sessionsToDeleteCount: sessionsToDeleteCount ?? 0, + themeError, isAuthenticating, isConfigInitialized, @@ -2334,9 +2309,7 @@ Logging in with Google... Restarting Gemini CLI to continue. }), [ isThemeDialogOpen, - shouldShowRetentionWarning, - retentionCheckComplete, - sessionsToDeleteCount, + themeError, isAuthenticating, isConfigInitialized, diff --git a/packages/cli/src/ui/components/DialogManager.tsx b/packages/cli/src/ui/components/DialogManager.tsx index 3cca19b0b0..c86a4ba8d3 100644 --- a/packages/cli/src/ui/components/DialogManager.tsx +++ b/packages/cli/src/ui/components/DialogManager.tsx @@ -37,9 +37,6 @@ import { AdminSettingsChangedDialog } from './AdminSettingsChangedDialog.js'; import { IdeTrustChangeDialog } from './IdeTrustChangeDialog.js'; import { NewAgentsNotification } from './NewAgentsNotification.js'; import { AgentConfigDialog } from './AgentConfigDialog.js'; -import { SessionRetentionWarningDialog } from './SessionRetentionWarningDialog.js'; -import { useCallback } from 'react'; -import { SettingScope } from '../../config/settings.js'; import { PolicyUpdateDialog } from './PolicyUpdateDialog.js'; interface DialogManagerProps { @@ -62,56 +59,8 @@ export const DialogManager = ({ terminalHeight, staticExtraHeight, terminalWidth: uiTerminalWidth, - shouldShowRetentionWarning, - sessionsToDeleteCount, } = uiState; - const handleKeep120Days = useCallback(() => { - settings.setValue( - SettingScope.User, - 'general.sessionRetention.warningAcknowledged', - true, - ); - settings.setValue( - SettingScope.User, - 'general.sessionRetention.enabled', - true, - ); - settings.setValue( - SettingScope.User, - 'general.sessionRetention.maxAge', - '120d', - ); - }, [settings]); - - const handleKeep30Days = useCallback(() => { - settings.setValue( - SettingScope.User, - 'general.sessionRetention.warningAcknowledged', - true, - ); - settings.setValue( - SettingScope.User, - 'general.sessionRetention.enabled', - true, - ); - settings.setValue( - SettingScope.User, - 'general.sessionRetention.maxAge', - '30d', - ); - }, [settings]); - - if (shouldShowRetentionWarning && sessionsToDeleteCount !== undefined) { - return ( - - ); - } - if (uiState.adminSettingsChanged) { return ; } diff --git a/packages/cli/src/ui/components/SessionRetentionWarningDialog.test.tsx b/packages/cli/src/ui/components/SessionRetentionWarningDialog.test.tsx deleted file mode 100644 index ec3157fa89..0000000000 --- a/packages/cli/src/ui/components/SessionRetentionWarningDialog.test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - * - * @license - */ - -import { describe, it, expect, vi, afterEach } from 'vitest'; -import { renderWithProviders } from '../../test-utils/render.js'; -import { SessionRetentionWarningDialog } from './SessionRetentionWarningDialog.js'; -import { waitFor } from '../../test-utils/async.js'; -import { act } from 'react'; - -// Helper to write to stdin -const writeKey = (stdin: { write: (data: string) => void }, key: string) => { - act(() => { - stdin.write(key); - }); -}; - -describe('SessionRetentionWarningDialog', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('renders correctly with warning message and session count', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( - , - ); - await waitUntilReady(); - - expect(lastFrame()).toContain('Keep chat history'); - expect(lastFrame()).toContain( - 'introducing a limit on how long chat sessions are stored', - ); - expect(lastFrame()).toContain('Keep for 30 days (Recommended)'); - expect(lastFrame()).toContain('42 sessions will be deleted'); - expect(lastFrame()).toContain('Keep for 120 days'); - expect(lastFrame()).toContain('No sessions will be deleted at this time'); - }); - - it('handles pluralization correctly for 1 session', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( - , - ); - await waitUntilReady(); - - expect(lastFrame()).toContain('1 session will be deleted'); - }); - - it('defaults to "Keep for 120 days" when there are sessions to delete', async () => { - const onKeep120Days = vi.fn(); - const onKeep30Days = vi.fn(); - - const { stdin, waitUntilReady } = renderWithProviders( - , - ); - await waitUntilReady(); - - // Initial selection should be "Keep for 120 days" (index 1) because count > 0 - // Pressing Enter immediately should select it. - writeKey(stdin, '\r'); - - await waitFor(() => { - expect(onKeep120Days).toHaveBeenCalled(); - expect(onKeep30Days).not.toHaveBeenCalled(); - }); - }); - - it('calls onKeep30Days when "Keep for 30 days" is explicitly selected (from 120 days default)', async () => { - const onKeep120Days = vi.fn(); - const onKeep30Days = vi.fn(); - - const { stdin, waitUntilReady } = renderWithProviders( - , - ); - await waitUntilReady(); - - // Default is index 1 (120 days). Move UP to index 0 (30 days). - writeKey(stdin, '\x1b[A'); // Up arrow - writeKey(stdin, '\r'); - - await waitFor(() => { - expect(onKeep30Days).toHaveBeenCalled(); - expect(onKeep120Days).not.toHaveBeenCalled(); - }); - }); - - it('should match snapshot', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( - , - ); - await waitUntilReady(); - - // Initial render - expect(lastFrame()).toMatchSnapshot(); - }); -}); diff --git a/packages/cli/src/ui/components/SessionRetentionWarningDialog.tsx b/packages/cli/src/ui/components/SessionRetentionWarningDialog.tsx deleted file mode 100644 index cd0477105c..0000000000 --- a/packages/cli/src/ui/components/SessionRetentionWarningDialog.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { Box, Text } from 'ink'; -import { theme } from '../semantic-colors.js'; -import { - RadioButtonSelect, - type RadioSelectItem, -} from './shared/RadioButtonSelect.js'; - -interface SessionRetentionWarningDialogProps { - onKeep120Days: () => void; - onKeep30Days: () => void; - sessionsToDeleteCount: number; -} - -export const SessionRetentionWarningDialog = ({ - onKeep120Days, - onKeep30Days, - sessionsToDeleteCount, -}: SessionRetentionWarningDialogProps) => { - const options: Array void>> = [ - { - label: 'Keep for 30 days (Recommended)', - value: onKeep30Days, - key: '30days', - sublabel: `${sessionsToDeleteCount} session${ - sessionsToDeleteCount === 1 ? '' : 's' - } will be deleted`, - }, - { - label: 'Keep for 120 days', - value: onKeep120Days, - key: '120days', - sublabel: 'No sessions will be deleted at this time', - }, - ]; - - return ( - - - Keep chat history - - - - - To keep your workspace clean, we are introducing a limit on how long - chat sessions are stored. Please choose a retention period for your - existing chats: - - - - - action()} - initialIndex={1} - /> - - - - - Set a custom limit /settings{' '} - and change "Keep chat history". - - - - ); -}; diff --git a/packages/cli/src/ui/components/__snapshots__/SessionRetentionWarningDialog.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/SessionRetentionWarningDialog.test.tsx.snap deleted file mode 100644 index 95f1b4760c..0000000000 --- a/packages/cli/src/ui/components/__snapshots__/SessionRetentionWarningDialog.test.tsx.snap +++ /dev/null @@ -1,21 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`SessionRetentionWarningDialog > should match snapshot 1`] = ` -"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ -│ Keep chat history │ -│ │ -│ To keep your workspace clean, we are introducing a limit on how long chat sessions are stored. │ -│ Please choose a retention period for your existing chats: │ -│ │ -│ │ -│ 1. Keep for 30 days (Recommended) │ -│ 123 sessions will be deleted │ -│ ● 2. Keep for 120 days │ -│ No sessions will be deleted at this time │ -│ │ -│ Set a custom limit /settings and change "Keep chat history". │ -│ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ -" -`; diff --git a/packages/cli/src/ui/contexts/UIStateContext.tsx b/packages/cli/src/ui/contexts/UIStateContext.tsx index 554cff34f9..ea9025aa6b 100644 --- a/packages/cli/src/ui/contexts/UIStateContext.tsx +++ b/packages/cli/src/ui/contexts/UIStateContext.tsx @@ -107,8 +107,6 @@ export interface UIState { history: HistoryItem[]; historyManager: UseHistoryManagerReturn; isThemeDialogOpen: boolean; - shouldShowRetentionWarning: boolean; - sessionsToDeleteCount: number; themeError: string | null; isAuthenticating: boolean; isConfigInitialized: boolean; diff --git a/packages/cli/src/ui/hooks/useSessionRetentionCheck.test.ts b/packages/cli/src/ui/hooks/useSessionRetentionCheck.test.ts deleted file mode 100644 index 67e5efbc6b..0000000000 --- a/packages/cli/src/ui/hooks/useSessionRetentionCheck.test.ts +++ /dev/null @@ -1,217 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { renderHook } from '../../test-utils/render.js'; -import { useSessionRetentionCheck } from './useSessionRetentionCheck.js'; -import { type Config } from '@google/gemini-cli-core'; -import type { Settings } from '../../config/settingsSchema.js'; -import { waitFor } from '../../test-utils/async.js'; - -// Mock utils -const mockGetAllSessionFiles = vi.fn(); -const mockIdentifySessionsToDelete = vi.fn(); - -vi.mock('../../utils/sessionUtils.js', () => ({ - getAllSessionFiles: () => mockGetAllSessionFiles(), -})); - -vi.mock('../../utils/sessionCleanup.js', () => ({ - identifySessionsToDelete: () => mockIdentifySessionsToDelete(), - DEFAULT_MIN_RETENTION: '30d', -})); - -describe('useSessionRetentionCheck', () => { - const mockConfig = { - storage: { - getProjectTempDir: () => '/mock/project/temp/dir', - }, - getSessionId: () => 'mock-session-id', - } as unknown as Config; - - beforeEach(() => { - vi.resetAllMocks(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('should show warning if enabled is true but maxAge is undefined', async () => { - const settings = { - general: { - sessionRetention: { - enabled: true, - maxAge: undefined, - warningAcknowledged: false, - }, - }, - } as unknown as Settings; - - mockGetAllSessionFiles.mockResolvedValue(['session1.json']); - mockIdentifySessionsToDelete.mockResolvedValue(['session1.json']); - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(true); - expect(mockGetAllSessionFiles).toHaveBeenCalled(); - expect(mockIdentifySessionsToDelete).toHaveBeenCalled(); - }); - }); - - it('should not show warning if warningAcknowledged is true', async () => { - const settings = { - general: { - sessionRetention: { - warningAcknowledged: true, - }, - }, - } as unknown as Settings; - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(false); - expect(mockGetAllSessionFiles).not.toHaveBeenCalled(); - expect(mockIdentifySessionsToDelete).not.toHaveBeenCalled(); - }); - }); - - it('should not show warning if retention is already enabled', async () => { - const settings = { - general: { - sessionRetention: { - enabled: true, - maxAge: '30d', // Explicitly enabled with non-default - }, - }, - } as unknown as Settings; - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(false); - expect(mockGetAllSessionFiles).not.toHaveBeenCalled(); - expect(mockIdentifySessionsToDelete).not.toHaveBeenCalled(); - }); - }); - - it('should show warning if sessions to delete exist', async () => { - const settings = { - general: { - sessionRetention: { - enabled: false, - warningAcknowledged: false, - }, - }, - } as unknown as Settings; - - mockGetAllSessionFiles.mockResolvedValue([ - 'session1.json', - 'session2.json', - ]); - mockIdentifySessionsToDelete.mockResolvedValue(['session1.json']); // 1 session to delete - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(true); - expect(result.current.sessionsToDeleteCount).toBe(1); - expect(mockGetAllSessionFiles).toHaveBeenCalled(); - expect(mockIdentifySessionsToDelete).toHaveBeenCalled(); - }); - }); - - it('should call onAutoEnable if no sessions to delete and currently disabled', async () => { - const settings = { - general: { - sessionRetention: { - enabled: false, - warningAcknowledged: false, - }, - }, - } as unknown as Settings; - - mockGetAllSessionFiles.mockResolvedValue(['session1.json']); - mockIdentifySessionsToDelete.mockResolvedValue([]); // 0 sessions to delete - - const onAutoEnable = vi.fn(); - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings, onAutoEnable), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(false); - expect(onAutoEnable).toHaveBeenCalled(); - }); - }); - - it('should not show warning if no sessions to delete', async () => { - const settings = { - general: { - sessionRetention: { - enabled: false, - warningAcknowledged: false, - }, - }, - } as unknown as Settings; - - mockGetAllSessionFiles.mockResolvedValue([ - 'session1.json', - 'session2.json', - ]); - mockIdentifySessionsToDelete.mockResolvedValue([]); // 0 sessions to delete - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(false); - expect(result.current.sessionsToDeleteCount).toBe(0); - expect(mockGetAllSessionFiles).toHaveBeenCalled(); - expect(mockIdentifySessionsToDelete).toHaveBeenCalled(); - }); - }); - - it('should handle errors gracefully (assume no warning)', async () => { - const settings = { - general: { - sessionRetention: { - enabled: false, - warningAcknowledged: false, - }, - }, - } as unknown as Settings; - - mockGetAllSessionFiles.mockRejectedValue(new Error('FS Error')); - - const { result } = renderHook(() => - useSessionRetentionCheck(mockConfig, settings), - ); - - await waitFor(() => { - expect(result.current.checkComplete).toBe(true); - expect(result.current.shouldShowWarning).toBe(false); - }); - }); -}); diff --git a/packages/cli/src/ui/hooks/useSessionRetentionCheck.ts b/packages/cli/src/ui/hooks/useSessionRetentionCheck.ts deleted file mode 100644 index 99b443cffc..0000000000 --- a/packages/cli/src/ui/hooks/useSessionRetentionCheck.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { useState, useEffect } from 'react'; -import { type Config } from '@google/gemini-cli-core'; -import { type Settings } from '../../config/settings.js'; -import { getAllSessionFiles } from '../../utils/sessionUtils.js'; -import { identifySessionsToDelete } from '../../utils/sessionCleanup.js'; -import path from 'node:path'; - -export function useSessionRetentionCheck( - config: Config, - settings: Settings, - onAutoEnable?: () => void, -) { - const [shouldShowWarning, setShouldShowWarning] = useState(false); - const [sessionsToDeleteCount, setSessionsToDeleteCount] = useState(0); - const [checkComplete, setCheckComplete] = useState(false); - - useEffect(() => { - // If warning already acknowledged or retention already enabled, skip check - if ( - settings.general?.sessionRetention?.warningAcknowledged || - (settings.general?.sessionRetention?.enabled && - settings.general?.sessionRetention?.maxAge !== undefined) - ) { - setShouldShowWarning(false); - setCheckComplete(true); - return; - } - - const checkSessions = async () => { - try { - const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats'); - const allFiles = await getAllSessionFiles( - chatsDir, - config.getSessionId(), - ); - - // Calculate how many sessions would be deleted if we applied a 30-day retention - const sessionsToDelete = await identifySessionsToDelete(allFiles, { - enabled: true, - maxAge: '30d', - }); - - if (sessionsToDelete.length > 0) { - setSessionsToDeleteCount(sessionsToDelete.length); - setShouldShowWarning(true); - } else { - setShouldShowWarning(false); - // If no sessions to delete, safe to auto-enable retention - onAutoEnable?.(); - } - } catch { - // If we can't check sessions, default to not showing the warning to be safe - setShouldShowWarning(false); - } finally { - setCheckComplete(true); - } - }; - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - checkSessions(); - }, [config, settings.general?.sessionRetention, onAutoEnable]); - - return { shouldShowWarning, checkComplete, sessionsToDeleteCount }; -} diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json index b93be1f0e7..c2919b5a7d 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -158,14 +158,15 @@ "enabled": { "title": "Enable Session Cleanup", "description": "Enable automatic session cleanup", - "markdownDescription": "Enable automatic session cleanup\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `false`", - "default": false, + "markdownDescription": "Enable automatic session cleanup\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `true`", + "default": true, "type": "boolean" }, "maxAge": { "title": "Keep chat history", "description": "Automatically delete chats older than this time period (e.g., \"30d\", \"7d\", \"24h\", \"1w\")", - "markdownDescription": "Automatically delete chats older than this time period (e.g., \"30d\", \"7d\", \"24h\", \"1w\")\n\n- Category: `General`\n- Requires restart: `no`", + "markdownDescription": "Automatically delete chats older than this time period (e.g., \"30d\", \"7d\", \"24h\", \"1w\")\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `30d`", + "default": "30d", "type": "string" }, "maxCount": { @@ -180,13 +181,6 @@ "markdownDescription": "Minimum retention period (safety limit, defaults to \"1d\")\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `1d`", "default": "1d", "type": "string" - }, - "warningAcknowledged": { - "title": "Warning Acknowledged", - "description": "INTERNAL: Whether the user has acknowledged the session retention warning", - "markdownDescription": "INTERNAL: Whether the user has acknowledged the session retention warning\n\n- Category: `General`\n- Requires restart: `no`\n- Default: `false`", - "default": false, - "type": "boolean" } }, "additionalProperties": false