mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-06 23:23:36 -07:00
e062f0d09a
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.
136 lines
4.1 KiB
TypeScript
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 };
|
|
}
|