feat(sessions): add /resume slash command to open the session browser (#13621)

This commit is contained in:
bl-ue
2025-11-25 11:54:09 -07:00
committed by GitHub
parent 098e5c281c
commit 94c3eecb99
16 changed files with 142 additions and 36 deletions
@@ -75,6 +75,7 @@ vi.mock('../ui/commands/modelCommand.js', () => ({
})); }));
vi.mock('../ui/commands/privacyCommand.js', () => ({ privacyCommand: {} })); vi.mock('../ui/commands/privacyCommand.js', () => ({ privacyCommand: {} }));
vi.mock('../ui/commands/quitCommand.js', () => ({ quitCommand: {} })); vi.mock('../ui/commands/quitCommand.js', () => ({ quitCommand: {} }));
vi.mock('../ui/commands/resumeCommand.js', () => ({ resumeCommand: {} }));
vi.mock('../ui/commands/statsCommand.js', () => ({ statsCommand: {} })); vi.mock('../ui/commands/statsCommand.js', () => ({ statsCommand: {} }));
vi.mock('../ui/commands/themeCommand.js', () => ({ themeCommand: {} })); vi.mock('../ui/commands/themeCommand.js', () => ({ themeCommand: {} }));
vi.mock('../ui/commands/toolsCommand.js', () => ({ toolsCommand: {} })); vi.mock('../ui/commands/toolsCommand.js', () => ({ toolsCommand: {} }));
@@ -32,6 +32,7 @@ import { policiesCommand } from '../ui/commands/policiesCommand.js';
import { profileCommand } from '../ui/commands/profileCommand.js'; import { profileCommand } from '../ui/commands/profileCommand.js';
import { quitCommand } from '../ui/commands/quitCommand.js'; import { quitCommand } from '../ui/commands/quitCommand.js';
import { restoreCommand } from '../ui/commands/restoreCommand.js'; import { restoreCommand } from '../ui/commands/restoreCommand.js';
import { resumeCommand } from '../ui/commands/resumeCommand.js';
import { statsCommand } from '../ui/commands/statsCommand.js'; import { statsCommand } from '../ui/commands/statsCommand.js';
import { themeCommand } from '../ui/commands/themeCommand.js'; import { themeCommand } from '../ui/commands/themeCommand.js';
import { toolsCommand } from '../ui/commands/toolsCommand.js'; import { toolsCommand } from '../ui/commands/toolsCommand.js';
@@ -82,6 +83,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
...(isDevelopment ? [profileCommand] : []), ...(isDevelopment ? [profileCommand] : []),
quitCommand, quitCommand,
restoreCommand(this.config), restoreCommand(this.config),
resumeCommand,
statsCommand, statsCommand,
themeCommand, themeCommand,
toolsCommand, toolsCommand,
+4
View File
@@ -148,6 +148,10 @@ const mockUIActions: UIActions = {
closeSettingsDialog: vi.fn(), closeSettingsDialog: vi.fn(),
closeModelDialog: vi.fn(), closeModelDialog: vi.fn(),
openPermissionsDialog: vi.fn(), openPermissionsDialog: vi.fn(),
openSessionBrowser: vi.fn(),
closeSessionBrowser: vi.fn(),
handleResumeSession: vi.fn(),
handleDeleteSession: vi.fn(),
closePermissionsDialog: vi.fn(), closePermissionsDialog: vi.fn(),
setShellModeActive: vi.fn(), setShellModeActive: vi.fn(),
vimHandleInput: vi.fn(), vimHandleInput: vi.fn(),
+31 -2
View File
@@ -99,6 +99,7 @@ import { type UpdateObject } from './utils/updateCheck.js';
import { setUpdateHandler } from '../utils/handleAutoUpdate.js'; import { setUpdateHandler } from '../utils/handleAutoUpdate.js';
import { registerCleanup, runExitCleanup } from '../utils/cleanup.js'; import { registerCleanup, runExitCleanup } from '../utils/cleanup.js';
import { RELAUNCH_EXIT_CODE } from '../utils/processUtils.js'; import { RELAUNCH_EXIT_CODE } from '../utils/processUtils.js';
import type { SessionInfo } from '../utils/sessionUtils.js';
import { useMessageQueue } from './hooks/useMessageQueue.js'; import { useMessageQueue } from './hooks/useMessageQueue.js';
import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js'; import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js';
import { useSessionStats } from './contexts/SessionContext.js'; import { useSessionStats } from './contexts/SessionContext.js';
@@ -108,9 +109,10 @@ import {
useExtensionUpdates, useExtensionUpdates,
} from './hooks/useExtensionUpdates.js'; } from './hooks/useExtensionUpdates.js';
import { ShellFocusContext } from './contexts/ShellFocusContext.js'; import { ShellFocusContext } from './contexts/ShellFocusContext.js';
import { useSessionResume } from './hooks/useSessionResume.js';
import { type ExtensionManager } from '../config/extension-manager.js'; import { type ExtensionManager } from '../config/extension-manager.js';
import { requestConsentInteractive } from '../config/extensions/consent.js'; 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 { useIncludeDirsTrust } from './hooks/useIncludeDirsTrust.js';
import { isWorkspaceTrusted } from '../config/trustedFolders.js'; import { isWorkspaceTrusted } from '../config/trustedFolders.js';
import { useAlternateBuffer } from './hooks/useAlternateBuffer.js'; import { useAlternateBuffer } from './hooks/useAlternateBuffer.js';
@@ -436,7 +438,7 @@ export const AppContainer = (props: AppContainerProps) => {
// Session browser and resume functionality // Session browser and resume functionality
const isGeminiClientInitialized = config.getGeminiClient()?.isInitialized(); const isGeminiClientInitialized = config.getGeminiClient()?.isInitialized();
useSessionResume({ const { loadHistoryForResume } = useSessionResume({
config, config,
historyManager, historyManager,
refreshStatic, refreshStatic,
@@ -445,6 +447,20 @@ export const AppContainer = (props: AppContainerProps) => {
resumedSessionData, resumedSessionData,
isAuthenticating, isAuthenticating,
}); });
const {
isSessionBrowserOpen,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession: handleDeleteSessionSync,
} = useSessionBrowser(config, loadHistoryForResume);
// Wrap handleDeleteSession to return a Promise for UIActions interface
const handleDeleteSession = useCallback(
async (session: SessionInfo): Promise<void> => {
handleDeleteSessionSync(session);
},
[handleDeleteSessionSync],
);
// Create handleAuthSelect wrapper for backward compatibility // Create handleAuthSelect wrapper for backward compatibility
const handleAuthSelect = useCallback( const handleAuthSelect = useCallback(
@@ -570,6 +586,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
openEditorDialog, openEditorDialog,
openPrivacyNotice: () => setShowPrivacyNotice(true), openPrivacyNotice: () => setShowPrivacyNotice(true),
openSettingsDialog, openSettingsDialog,
openSessionBrowser,
openModelDialog, openModelDialog,
openPermissionsDialog, openPermissionsDialog,
quit: (messages: HistoryItem[]) => { quit: (messages: HistoryItem[]) => {
@@ -590,6 +607,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
openThemeDialog, openThemeDialog,
openEditorDialog, openEditorDialog,
openSettingsDialog, openSettingsDialog,
openSessionBrowser,
openModelDialog, openModelDialog,
setQuittingMessages, setQuittingMessages,
setDebugMessage, setDebugMessage,
@@ -1330,6 +1348,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
showPrivacyNotice || showPrivacyNotice ||
showIdeRestartPrompt || showIdeRestartPrompt ||
!!proQuotaRequest || !!proQuotaRequest ||
isSessionBrowserOpen ||
isAuthDialogOpen || isAuthDialogOpen ||
authState === AuthState.AwaitingApiKeyInput; authState === AuthState.AwaitingApiKeyInput;
@@ -1402,6 +1421,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
debugMessage, debugMessage,
quittingMessages, quittingMessages,
isSettingsDialogOpen, isSettingsDialogOpen,
isSessionBrowserOpen,
isModelDialogOpen, isModelDialogOpen,
isPermissionsDialogOpen, isPermissionsDialogOpen,
permissionsDialogProps, permissionsDialogProps,
@@ -1492,6 +1512,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
debugMessage, debugMessage,
quittingMessages, quittingMessages,
isSettingsDialogOpen, isSettingsDialogOpen,
isSessionBrowserOpen,
isModelDialogOpen, isModelDialogOpen,
isPermissionsDialogOpen, isPermissionsDialogOpen,
permissionsDialogProps, permissionsDialogProps,
@@ -1601,6 +1622,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleFinalSubmit, handleFinalSubmit,
handleClearScreen, handleClearScreen,
handleProQuotaChoice, handleProQuotaChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession,
setQueueErrorMessage, setQueueErrorMessage,
popAllMessages, popAllMessages,
handleApiKeySubmit, handleApiKeySubmit,
@@ -1632,6 +1657,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleFinalSubmit, handleFinalSubmit,
handleClearScreen, handleClearScreen,
handleProQuotaChoice, handleProQuotaChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession,
setQueueErrorMessage, setQueueErrorMessage,
popAllMessages, popAllMessages,
handleApiKeySubmit, handleApiKeySubmit,
@@ -0,0 +1,25 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {
OpenDialogActionReturn,
CommandContext,
SlashCommand,
} from './types.js';
import { CommandKind } from './types.js';
export const resumeCommand: SlashCommand = {
name: 'resume',
description: 'Browse and resume auto-saved conversations',
kind: CommandKind.BUILT_IN,
action: async (
_context: CommandContext,
_args: string,
): Promise<OpenDialogActionReturn> => ({
type: 'dialog',
dialog: 'sessionBrowser',
}),
};
+1
View File
@@ -123,6 +123,7 @@ export interface OpenDialogActionReturn {
| 'editor' | 'editor'
| 'privacy' | 'privacy'
| 'settings' | 'settings'
| 'sessionBrowser'
| 'model' | 'model'
| 'permissions'; | 'permissions';
} }
@@ -20,6 +20,7 @@ import { PrivacyNotice } from '../privacy/PrivacyNotice.js';
import { ProQuotaDialog } from './ProQuotaDialog.js'; import { ProQuotaDialog } from './ProQuotaDialog.js';
import { runExitCleanup } from '../../utils/cleanup.js'; import { runExitCleanup } from '../../utils/cleanup.js';
import { RELAUNCH_EXIT_CODE } from '../../utils/processUtils.js'; import { RELAUNCH_EXIT_CODE } from '../../utils/processUtils.js';
import { SessionBrowser } from './SessionBrowser.js';
import { PermissionsModifyTrustDialog } from './PermissionsModifyTrustDialog.js'; import { PermissionsModifyTrustDialog } from './PermissionsModifyTrustDialog.js';
import { ModelDialog } from './ModelDialog.js'; import { ModelDialog } from './ModelDialog.js';
import { theme } from '../semantic-colors.js'; import { theme } from '../semantic-colors.js';
@@ -210,6 +211,16 @@ export const DialogManager = ({
/> />
); );
} }
if (uiState.isSessionBrowserOpen) {
return (
<SessionBrowser
config={config}
onResumeSession={uiActions.handleResumeSession}
onDeleteSession={uiActions.handleDeleteSession}
onExit={uiActions.closeSessionBrowser}
/>
);
}
if (uiState.isPermissionsDialogOpen) { if (uiState.isPermissionsDialogOpen) {
return ( return (
@@ -86,6 +86,12 @@ const mockSlashCommands: SlashCommand[] = [
}, },
], ],
}, },
{
name: 'resume',
description: 'Browse and resume sessions',
kind: CommandKind.BUILT_IN,
action: vi.fn(),
},
]; ];
describe('InputPrompt', () => { describe('InputPrompt', () => {
@@ -57,7 +57,10 @@ vi.mock('./SessionBrowser.js', async (importOriginal) => {
moveSelection, moveSelection,
cycleSortOrder, cycleSortOrder,
props.onResumeSession, props.onResumeSession,
props.onDeleteSession, props.onDeleteSession ??
(async () => {
// no-op delete handler for tests that don't care about deletion
}),
props.onExit, props.onExit,
); );
@@ -146,12 +149,14 @@ describe('SessionBrowser component', () => {
it('shows empty state when no sessions exist', () => { it('shows empty state when no sessions exist', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
const { lastFrame } = render( const { lastFrame } = render(
<TestSessionBrowser <TestSessionBrowser
config={config} config={config}
onResumeSession={onResumeSession} onResumeSession={onResumeSession}
onDeleteSession={onDeleteSession}
onExit={onExit} onExit={onExit}
testSessions={[]} testSessions={[]}
/>, />,
@@ -181,12 +186,14 @@ describe('SessionBrowser component', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
const { lastFrame } = render( const { lastFrame } = render(
<TestSessionBrowser <TestSessionBrowser
config={config} config={config}
onResumeSession={onResumeSession} onResumeSession={onResumeSession}
onDeleteSession={onDeleteSession}
onExit={onExit} onExit={onExit}
testSessions={[session1, session2]} testSessions={[session1, session2]}
/>, />,
@@ -230,12 +237,14 @@ describe('SessionBrowser component', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
const { lastFrame } = render( const { lastFrame } = render(
<TestSessionBrowser <TestSessionBrowser
config={config} config={config}
onResumeSession={onResumeSession} onResumeSession={onResumeSession}
onDeleteSession={onDeleteSession}
onExit={onExit} onExit={onExit}
testSessions={[searchSession, otherSession]} testSessions={[searchSession, otherSession]}
/>, />,
@@ -279,12 +288,14 @@ describe('SessionBrowser component', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
const { lastFrame } = render( const { lastFrame } = render(
<TestSessionBrowser <TestSessionBrowser
config={config} config={config}
onResumeSession={onResumeSession} onResumeSession={onResumeSession}
onDeleteSession={onDeleteSession}
onExit={onExit} onExit={onExit}
testSessions={[session1, session2]} testSessions={[session1, session2]}
/>, />,
@@ -323,7 +334,7 @@ describe('SessionBrowser component', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn(); const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
render( render(
@@ -348,12 +359,14 @@ describe('SessionBrowser component', () => {
it('shows an error state when loading sessions fails', () => { it('shows an error state when loading sessions fails', () => {
const config = createMockConfig(); const config = createMockConfig();
const onResumeSession = vi.fn(); const onResumeSession = vi.fn();
const onDeleteSession = vi.fn().mockResolvedValue(undefined);
const onExit = vi.fn(); const onExit = vi.fn();
const { lastFrame } = render( const { lastFrame } = render(
<TestSessionBrowser <TestSessionBrowser
config={config} config={config}
onResumeSession={onResumeSession} onResumeSession={onResumeSession}
onDeleteSession={onDeleteSession}
onExit={onExit} onExit={onExit}
testError="storage failure" testError="storage failure"
/>, />,
@@ -28,7 +28,7 @@ export interface SessionBrowserProps {
/** Callback when user selects a session to resume */ /** Callback when user selects a session to resume */
onResumeSession: (session: SessionInfo) => void; onResumeSession: (session: SessionInfo) => void;
/** Callback when user deletes a session */ /** Callback when user deletes a session */
onDeleteSession?: (session: SessionInfo) => void; onDeleteSession: (session: SessionInfo) => Promise<void>;
/** Callback when user exits the session browser */ /** Callback when user exits the session browser */
onExit: () => void; onExit: () => void;
} }
@@ -463,9 +463,11 @@ const SessionItem = ({
} }
} }
// Reserve a few characters for metadata like " (current)" so the name doesn't wrap awkwardly.
const reservedForMeta = additionalInfo ? additionalInfo.length + 1 : 0;
const availableMessageWidth = Math.max( const availableMessageWidth = Math.max(
20, 20,
terminalWidth - FIXED_SESSION_COLUMNS_WIDTH, terminalWidth - FIXED_SESSION_COLUMNS_WIDTH - reservedForMeta,
); );
const truncatedMessage = const truncatedMessage =
@@ -759,7 +761,7 @@ export const useSessionBrowserInput = (
moveSelection: (delta: number) => void, moveSelection: (delta: number) => void,
cycleSortOrder: () => void, cycleSortOrder: () => void,
onResumeSession: (session: SessionInfo) => void, onResumeSession: (session: SessionInfo) => void,
onDeleteSession: ((session: SessionInfo) => void) | undefined, onDeleteSession: (session: SessionInfo) => Promise<void>,
onExit: () => void, onExit: () => void,
) => { ) => {
useKeypress( useKeypress(
@@ -817,13 +819,9 @@ export const useSessionBrowserInput = (
else if (key.sequence === 'x' || key.sequence === 'X') { else if (key.sequence === 'x' || key.sequence === 'X') {
const selectedSession = const selectedSession =
state.filteredAndSortedSessions[state.activeIndex]; state.filteredAndSortedSessions[state.activeIndex];
if ( if (selectedSession && !selectedSession.isCurrentSession) {
selectedSession && onDeleteSession(selectedSession)
!selectedSession.isCurrentSession && .then(() => {
onDeleteSession
) {
try {
onDeleteSession(selectedSession);
// Remove the session from the state // Remove the session from the state
state.setSessions( state.setSessions(
state.sessions.filter((s) => s.id !== selectedSession.id), state.sessions.filter((s) => s.id !== selectedSession.id),
@@ -838,17 +836,18 @@ export const useSessionBrowserInput = (
Math.max(0, state.filteredAndSortedSessions.length - 2), Math.max(0, state.filteredAndSortedSessions.length - 2),
); );
} }
} catch (error) { })
.catch((error) => {
state.setError( state.setError(
`Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`, `Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`,
); );
} });
} }
} }
// less-like u/d controls. // less-like u/d controls.
else if (key.sequence === 'd') { else if (key.sequence === 'u') {
moveSelection(-Math.round(SESSIONS_PER_PAGE / 2)); moveSelection(-Math.round(SESSIONS_PER_PAGE / 2));
} else if (key.sequence === 'u') { } else if (key.sequence === 'd') {
moveSelection(Math.round(SESSIONS_PER_PAGE / 2)); moveSelection(Math.round(SESSIONS_PER_PAGE / 2));
} }
} }
@@ -12,6 +12,7 @@ import { type AuthType, type EditorType } from '@google/gemini-cli-core';
import { type LoadableSettingScope } from '../../config/settings.js'; import { type LoadableSettingScope } from '../../config/settings.js';
import type { AuthState } from '../types.js'; import type { AuthState } from '../types.js';
import { type PermissionsDialogProps } from '../components/PermissionsModifyTrustDialog.js'; import { type PermissionsDialogProps } from '../components/PermissionsModifyTrustDialog.js';
import type { SessionInfo } from '../../utils/sessionUtils.js';
export interface UIActions { export interface UIActions {
handleThemeSelect: (themeName: string, scope: LoadableSettingScope) => void; handleThemeSelect: (themeName: string, scope: LoadableSettingScope) => void;
@@ -45,6 +46,10 @@ export interface UIActions {
handleProQuotaChoice: ( handleProQuotaChoice: (
choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade', choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade',
) => void; ) => void;
openSessionBrowser: () => void;
closeSessionBrowser: () => void;
handleResumeSession: (session: SessionInfo) => Promise<void>;
handleDeleteSession: (session: SessionInfo) => Promise<void>;
setQueueErrorMessage: (message: string | null) => void; setQueueErrorMessage: (message: string | null) => void;
popAllMessages: (onPop: (messages: string | undefined) => void) => void; popAllMessages: (onPop: (messages: string | undefined) => void) => void;
handleApiKeySubmit: (apiKey: string) => Promise<void>; handleApiKeySubmit: (apiKey: string) => Promise<void>;
@@ -59,6 +59,7 @@ export interface UIState {
debugMessage: string; debugMessage: string;
quittingMessages: HistoryItem[] | null; quittingMessages: HistoryItem[] | null;
isSettingsDialogOpen: boolean; isSettingsDialogOpen: boolean;
isSessionBrowserOpen: boolean;
isModelDialogOpen: boolean; isModelDialogOpen: boolean;
isPermissionsDialogOpen: boolean; isPermissionsDialogOpen: boolean;
permissionsDialogProps: { targetDirectory?: string } | null; permissionsDialogProps: { targetDirectory?: string } | null;
@@ -190,6 +190,7 @@ describe('useSlashCommandProcessor', () => {
openEditorDialog: vi.fn(), openEditorDialog: vi.fn(),
openPrivacyNotice: vi.fn(), openPrivacyNotice: vi.fn(),
openSettingsDialog: vi.fn(), openSettingsDialog: vi.fn(),
openSessionBrowser: vi.fn(),
openModelDialog: mockOpenModelDialog, openModelDialog: mockOpenModelDialog,
openPermissionsDialog: vi.fn(), openPermissionsDialog: vi.fn(),
quit: mockSetQuittingMessages, quit: mockSetQuittingMessages,
@@ -52,6 +52,7 @@ interface SlashCommandProcessorActions {
openEditorDialog: () => void; openEditorDialog: () => void;
openPrivacyNotice: () => void; openPrivacyNotice: () => void;
openSettingsDialog: () => void; openSettingsDialog: () => void;
openSessionBrowser: () => void;
openModelDialog: () => void; openModelDialog: () => void;
openPermissionsDialog: (props?: { targetDirectory?: string }) => void; openPermissionsDialog: (props?: { targetDirectory?: string }) => void;
quit: (messages: HistoryItem[]) => void; quit: (messages: HistoryItem[]) => void;
@@ -410,6 +411,9 @@ export const useSlashCommandProcessor = (
case 'privacy': case 'privacy':
actions.openPrivacyNotice(); actions.openPrivacyNotice();
return { type: 'handled' }; return { type: 'handled' };
case 'sessionBrowser':
actions.openSessionBrowser();
return { type: 'handled' };
case 'settings': case 'settings':
actions.openSettingsDialog(); actions.openSettingsDialog();
return { type: 'handled' }; return { type: 'handled' };
@@ -91,12 +91,16 @@ export const useSessionBrowser = (
*/ */
handleDeleteSession: useCallback( handleDeleteSession: useCallback(
(session: SessionInfo) => { (session: SessionInfo) => {
// Note: Chat sessions are stored on disk using a filename derived from
// the session, e.g. "session-<timestamp>-<sessionIdPrefix>.json".
// The ChatRecordingService.deleteSession API expects this file basename
// (without the ".json" extension), not the full session UUID.
try { try {
const chatRecordingService = config const chatRecordingService = config
.getGeminiClient() .getGeminiClient()
?.getChatRecordingService(); ?.getChatRecordingService();
if (chatRecordingService) { if (chatRecordingService) {
chatRecordingService.deleteSession(session.id); chatRecordingService.deleteSession(session.file);
} }
} catch (error) { } catch (error) {
console.error('Error deleting session:', error); console.error('Error deleting session:', error);
+1 -1
View File
@@ -469,7 +469,7 @@ export class GeminiChat {
: undefined, : undefined,
}); });
return this.processStreamResponse(model, streamResponse); return this.processStreamResponse(effectiveModel, streamResponse);
} }
/** /**