Files
gemini-cli/packages/cli/src/ui/hooks/useSessionResume.ts
T
Sandy Tao e062f0d09a perf: skip pre-compression history on session resume
On resume (-r), the CLI was loading and replaying the entire session
recording, including messages that had already been compressed away.
For long-running Forever Mode sessions this made resume extremely slow.

Add lastCompressionIndex to ConversationRecord, stamped when
compression succeeds. On resume, only messages from that index
onward are loaded into the client history and UI. Fully backward
compatible — old sessions without the field load all messages as before.
2026-03-06 22:03:45 -08:00

136 lines
4.1 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useCallback, useEffect, useRef, useState } from 'react';
import {
coreEvents,
type Config,
type ResumedSessionData,
convertSessionToClientHistory,
} from '@google/gemini-cli-core';
import type { Part } from '@google/genai';
import type { HistoryItemWithoutId } from '../types.js';
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
import { convertSessionToHistoryFormats } from './useSessionBrowser.js';
interface UseSessionResumeParams {
config: Config;
historyManager: UseHistoryManagerReturn;
refreshStatic: () => void;
isGeminiClientInitialized: boolean;
setQuittingMessages: (messages: null) => void;
resumedSessionData?: ResumedSessionData;
isAuthenticating: boolean;
}
/**
* Hook to handle session resumption logic.
* Provides a callback to load history for resume and automatically
* handles command-line resume on mount.
*/
export function useSessionResume({
config,
historyManager,
refreshStatic,
isGeminiClientInitialized,
setQuittingMessages,
resumedSessionData,
isAuthenticating,
}: UseSessionResumeParams) {
const [isResuming, setIsResuming] = useState(false);
// Use refs to avoid dependency chain that causes infinite loop
const historyManagerRef = useRef(historyManager);
const refreshStaticRef = useRef(refreshStatic);
useEffect(() => {
historyManagerRef.current = historyManager;
refreshStaticRef.current = refreshStatic;
});
const loadHistoryForResume = useCallback(
async (
uiHistory: HistoryItemWithoutId[],
clientHistory: Array<{ role: 'user' | 'model'; parts: Part[] }>,
resumedData: ResumedSessionData,
) => {
// Wait for the client.
if (!isGeminiClientInitialized) {
return;
}
setIsResuming(true);
try {
// Now that we have the client, load the history into the UI and the client.
setQuittingMessages(null);
historyManagerRef.current.clearItems();
uiHistory.forEach((item, index) => {
historyManagerRef.current.addItem(item, index, true);
});
refreshStaticRef.current(); // Force Static component to re-render with the updated history.
// Restore directories from the resumed session
if (
resumedData.conversation.directories &&
resumedData.conversation.directories.length > 0
) {
const workspaceContext = config.getWorkspaceContext();
// Add back any directories that were saved in the session
// but filter out ones that no longer exist
workspaceContext.addDirectories(resumedData.conversation.directories);
}
// Give the history to the Gemini client.
await config.getGeminiClient()?.resumeChat(clientHistory, resumedData);
} catch (error) {
coreEvents.emitFeedback(
'error',
'Failed to resume session. Please try again.',
error,
);
} finally {
setIsResuming(false);
}
},
[config, isGeminiClientInitialized, setQuittingMessages],
);
// Handle interactive resume from the command line (-r/--resume without -p/--prompt-interactive).
// Only if we're not authenticating and the client is initialized, though.
const hasLoadedResumedSession = useRef(false);
useEffect(() => {
if (
resumedSessionData &&
!isAuthenticating &&
isGeminiClientInitialized &&
!hasLoadedResumedSession.current
) {
hasLoadedResumedSession.current = true;
const compressionIndex =
resumedSessionData.conversation.lastCompressionIndex;
const historyData = convertSessionToHistoryFormats(
resumedSessionData.conversation.messages,
compressionIndex,
);
void loadHistoryForResume(
historyData.uiHistory,
convertSessionToClientHistory(
resumedSessionData.conversation.messages,
compressionIndex,
),
resumedSessionData,
);
}
}, [
resumedSessionData,
isAuthenticating,
isGeminiClientInitialized,
loadHistoryForResume,
]);
return { loadHistoryForResume, isResuming };
}