mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
feat(sessions): record interactive-only errors and warnings to chat recording JSON files (#13300)
This commit is contained in:
@@ -1392,7 +1392,12 @@ describe('AppContainer State Management', () => {
|
|||||||
pressKey({ name: 'c', ctrl: true }, 2);
|
pressKey({ name: 'c', ctrl: true }, 2);
|
||||||
|
|
||||||
expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(2);
|
expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(2);
|
||||||
expect(mockHandleSlashCommand).toHaveBeenCalledWith('/quit');
|
expect(mockHandleSlashCommand).toHaveBeenCalledWith(
|
||||||
|
'/quit',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
);
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1432,7 +1437,12 @@ describe('AppContainer State Management', () => {
|
|||||||
|
|
||||||
pressKey({ name: 'd', ctrl: true }, 2);
|
pressKey({ name: 'd', ctrl: true }, 2);
|
||||||
|
|
||||||
expect(mockHandleSlashCommand).toHaveBeenCalledWith('/quit');
|
expect(mockHandleSlashCommand).toHaveBeenCalledWith(
|
||||||
|
'/quit',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
);
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,9 @@ const SHELL_HEIGHT_PADDING = 10;
|
|||||||
|
|
||||||
export const AppContainer = (props: AppContainerProps) => {
|
export const AppContainer = (props: AppContainerProps) => {
|
||||||
const { config, initializationResult, resumedSessionData } = props;
|
const { config, initializationResult, resumedSessionData } = props;
|
||||||
const historyManager = useHistory();
|
const historyManager = useHistory({
|
||||||
|
chatRecordingService: config.getGeminiClient()?.getChatRecordingService(),
|
||||||
|
});
|
||||||
useMemoryMonitor(historyManager);
|
useMemoryMonitor(historyManager);
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const isAlternateBuffer = useAlternateBuffer();
|
const isAlternateBuffer = useAlternateBuffer();
|
||||||
@@ -1026,7 +1028,7 @@ Logging in with Google... Please restart Gemini CLI to continue.
|
|||||||
recordExitFail(config);
|
recordExitFail(config);
|
||||||
}
|
}
|
||||||
if (ctrlCPressCount > 1) {
|
if (ctrlCPressCount > 1) {
|
||||||
handleSlashCommand('/quit');
|
handleSlashCommand('/quit', undefined, undefined, false);
|
||||||
} else {
|
} else {
|
||||||
ctrlCTimerRef.current = setTimeout(() => {
|
ctrlCTimerRef.current = setTimeout(() => {
|
||||||
setCtrlCPressCount(0);
|
setCtrlCPressCount(0);
|
||||||
@@ -1044,7 +1046,7 @@ Logging in with Google... Please restart Gemini CLI to continue.
|
|||||||
recordExitFail(config);
|
recordExitFail(config);
|
||||||
}
|
}
|
||||||
if (ctrlDPressCount > 1) {
|
if (ctrlDPressCount > 1) {
|
||||||
handleSlashCommand('/quit');
|
handleSlashCommand('/quit', undefined, undefined, false);
|
||||||
} else {
|
} else {
|
||||||
ctrlDTimerRef.current = setTimeout(() => {
|
ctrlDTimerRef.current = setTimeout(() => {
|
||||||
setCtrlDPressCount(0);
|
setCtrlDPressCount(0);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ describe('clearCommand', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockResetChat = vi.fn().mockResolvedValue(undefined);
|
mockResetChat = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const mockGetChatRecordingService = vi.fn();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|
||||||
mockContext = createMockCommandContext({
|
mockContext = createMockCommandContext({
|
||||||
@@ -38,7 +39,11 @@ describe('clearCommand', () => {
|
|||||||
getGeminiClient: () =>
|
getGeminiClient: () =>
|
||||||
({
|
({
|
||||||
resetChat: mockResetChat,
|
resetChat: mockResetChat,
|
||||||
|
getChat: () => ({
|
||||||
|
getChatRecordingService: mockGetChatRecordingService,
|
||||||
|
}),
|
||||||
}) as unknown as GeminiClient,
|
}) as unknown as GeminiClient,
|
||||||
|
setSessionId: vi.fn(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { uiTelemetryService } from '@google/gemini-cli-core';
|
import { uiTelemetryService } from '@google/gemini-cli-core';
|
||||||
import type { SlashCommand } from './types.js';
|
import type { SlashCommand } from './types.js';
|
||||||
import { CommandKind } from './types.js';
|
import { CommandKind } from './types.js';
|
||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
export const clearCommand: SlashCommand = {
|
export const clearCommand: SlashCommand = {
|
||||||
name: 'clear',
|
name: 'clear',
|
||||||
@@ -14,6 +15,11 @@ export const clearCommand: SlashCommand = {
|
|||||||
kind: CommandKind.BUILT_IN,
|
kind: CommandKind.BUILT_IN,
|
||||||
action: async (context, _args) => {
|
action: async (context, _args) => {
|
||||||
const geminiClient = context.services.config?.getGeminiClient();
|
const geminiClient = context.services.config?.getGeminiClient();
|
||||||
|
const config = context.services.config;
|
||||||
|
const chatRecordingService = context.services.config
|
||||||
|
?.getGeminiClient()
|
||||||
|
?.getChat()
|
||||||
|
.getChatRecordingService();
|
||||||
|
|
||||||
if (geminiClient) {
|
if (geminiClient) {
|
||||||
context.ui.setDebugMessage('Clearing terminal and resetting chat.');
|
context.ui.setDebugMessage('Clearing terminal and resetting chat.');
|
||||||
@@ -24,6 +30,13 @@ export const clearCommand: SlashCommand = {
|
|||||||
context.ui.setDebugMessage('Clearing terminal.');
|
context.ui.setDebugMessage('Clearing terminal.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start a new conversation recording with a new session ID
|
||||||
|
if (config && chatRecordingService) {
|
||||||
|
const newSessionId = randomUUID();
|
||||||
|
config.setSessionId(newSessionId);
|
||||||
|
chatRecordingService.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
uiTelemetryService.setLastPromptTokenCount(0);
|
uiTelemetryService.setLastPromptTokenCount(0);
|
||||||
context.ui.clear();
|
context.ui.clear();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -311,6 +311,7 @@ export const useSlashCommandProcessor = (
|
|||||||
rawQuery: PartListUnion,
|
rawQuery: PartListUnion,
|
||||||
oneTimeShellAllowlist?: Set<string>,
|
oneTimeShellAllowlist?: Set<string>,
|
||||||
overwriteConfirmed?: boolean,
|
overwriteConfirmed?: boolean,
|
||||||
|
addToHistory: boolean = true,
|
||||||
): Promise<SlashCommandProcessorResult | false> => {
|
): Promise<SlashCommandProcessorResult | false> => {
|
||||||
if (!commands) {
|
if (!commands) {
|
||||||
return false;
|
return false;
|
||||||
@@ -326,8 +327,13 @@ export const useSlashCommandProcessor = (
|
|||||||
|
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
|
|
||||||
const userMessageTimestamp = Date.now();
|
if (addToHistory) {
|
||||||
addItem({ type: MessageType.USER, text: trimmed }, userMessageTimestamp);
|
const userMessageTimestamp = Date.now();
|
||||||
|
addItem(
|
||||||
|
{ type: MessageType.USER, text: trimmed },
|
||||||
|
userMessageTimestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ import type {
|
|||||||
LoadedSettings,
|
LoadedSettings,
|
||||||
} from '../../config/settings.js';
|
} from '../../config/settings.js';
|
||||||
import { SettingScope } from '../../config/settings.js';
|
import { SettingScope } from '../../config/settings.js';
|
||||||
import { MessageType, type HistoryItem } from '../types.js';
|
import { MessageType } from '../types.js';
|
||||||
import {
|
import {
|
||||||
type EditorType,
|
type EditorType,
|
||||||
checkHasEditorType,
|
checkHasEditorType,
|
||||||
allowEditorTypeInSandbox,
|
allowEditorTypeInSandbox,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
|
|
||||||
import { SettingPaths } from '../../config/settingPaths.js';
|
import { SettingPaths } from '../../config/settingPaths.js';
|
||||||
|
|
||||||
@@ -45,9 +46,7 @@ const mockAllowEditorTypeInSandbox = vi.mocked(allowEditorTypeInSandbox);
|
|||||||
describe('useEditorSettings', () => {
|
describe('useEditorSettings', () => {
|
||||||
let mockLoadedSettings: LoadedSettings;
|
let mockLoadedSettings: LoadedSettings;
|
||||||
let mockSetEditorError: MockedFunction<(error: string | null) => void>;
|
let mockSetEditorError: MockedFunction<(error: string | null) => void>;
|
||||||
let mockAddItem: MockedFunction<
|
let mockAddItem: MockedFunction<UseHistoryManagerReturn['addItem']>;
|
||||||
(item: Omit<HistoryItem, 'id'>, timestamp: number) => void
|
|
||||||
>;
|
|
||||||
let result: ReturnType<typeof useEditorSettings>;
|
let result: ReturnType<typeof useEditorSettings>;
|
||||||
|
|
||||||
function TestComponent() {
|
function TestComponent() {
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ import type {
|
|||||||
LoadableSettingScope,
|
LoadableSettingScope,
|
||||||
LoadedSettings,
|
LoadedSettings,
|
||||||
} from '../../config/settings.js';
|
} from '../../config/settings.js';
|
||||||
import { type HistoryItem, MessageType } from '../types.js';
|
import { MessageType } from '../types.js';
|
||||||
import type { EditorType } from '@google/gemini-cli-core';
|
import type { EditorType } from '@google/gemini-cli-core';
|
||||||
import {
|
import {
|
||||||
allowEditorTypeInSandbox,
|
allowEditorTypeInSandbox,
|
||||||
checkHasEditorType,
|
checkHasEditorType,
|
||||||
getEditorDisplayName,
|
getEditorDisplayName,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
|
|
||||||
import { SettingPaths } from '../../config/settingPaths.js';
|
import { SettingPaths } from '../../config/settingPaths.js';
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ interface UseEditorSettingsReturn {
|
|||||||
export const useEditorSettings = (
|
export const useEditorSettings = (
|
||||||
loadedSettings: LoadedSettings,
|
loadedSettings: LoadedSettings,
|
||||||
setEditorError: (error: string | null) => void,
|
setEditorError: (error: string | null) => void,
|
||||||
addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
|
addItem: UseHistoryManagerReturn['addItem'],
|
||||||
): UseEditorSettingsReturn => {
|
): UseEditorSettingsReturn => {
|
||||||
const [isEditorDialogOpen, setIsEditorDialogOpen] = useState(false);
|
const [isEditorDialogOpen, setIsEditorDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import { useState, useRef, useCallback, useMemo } from 'react';
|
import { useState, useRef, useCallback, useMemo } from 'react';
|
||||||
import type { HistoryItem } from '../types.js';
|
import type { HistoryItem } from '../types.js';
|
||||||
|
import type { ChatRecordingService } from '@google/gemini-cli-core/src/services/chatRecordingService.js';
|
||||||
|
|
||||||
// Type for the updater function passed to updateHistoryItem
|
// Type for the updater function passed to updateHistoryItem
|
||||||
type HistoryItemUpdater = (
|
type HistoryItemUpdater = (
|
||||||
@@ -14,7 +15,11 @@ type HistoryItemUpdater = (
|
|||||||
|
|
||||||
export interface UseHistoryManagerReturn {
|
export interface UseHistoryManagerReturn {
|
||||||
history: HistoryItem[];
|
history: HistoryItem[];
|
||||||
addItem: (itemData: Omit<HistoryItem, 'id'>, baseTimestamp: number) => number; // Returns the generated ID
|
addItem: (
|
||||||
|
itemData: Omit<HistoryItem, 'id'>,
|
||||||
|
baseTimestamp: number,
|
||||||
|
isResuming?: boolean,
|
||||||
|
) => number; // Returns the generated ID
|
||||||
updateItem: (
|
updateItem: (
|
||||||
id: number,
|
id: number,
|
||||||
updates: Partial<Omit<HistoryItem, 'id'>> | HistoryItemUpdater,
|
updates: Partial<Omit<HistoryItem, 'id'>> | HistoryItemUpdater,
|
||||||
@@ -29,7 +34,11 @@ export interface UseHistoryManagerReturn {
|
|||||||
* Encapsulates the history array, message ID generation, adding items,
|
* Encapsulates the history array, message ID generation, adding items,
|
||||||
* updating items, and clearing the history.
|
* updating items, and clearing the history.
|
||||||
*/
|
*/
|
||||||
export function useHistory(): UseHistoryManagerReturn {
|
export function useHistory({
|
||||||
|
chatRecordingService,
|
||||||
|
}: {
|
||||||
|
chatRecordingService?: ChatRecordingService | null;
|
||||||
|
} = {}): UseHistoryManagerReturn {
|
||||||
const [history, setHistory] = useState<HistoryItem[]>([]);
|
const [history, setHistory] = useState<HistoryItem[]>([]);
|
||||||
const messageIdCounterRef = useRef(0);
|
const messageIdCounterRef = useRef(0);
|
||||||
|
|
||||||
@@ -45,7 +54,11 @@ export function useHistory(): UseHistoryManagerReturn {
|
|||||||
|
|
||||||
// Adds a new item to the history state with a unique ID.
|
// Adds a new item to the history state with a unique ID.
|
||||||
const addItem = useCallback(
|
const addItem = useCallback(
|
||||||
(itemData: Omit<HistoryItem, 'id'>, baseTimestamp: number): number => {
|
(
|
||||||
|
itemData: Omit<HistoryItem, 'id'>,
|
||||||
|
baseTimestamp: number,
|
||||||
|
isResuming: boolean = false,
|
||||||
|
): number => {
|
||||||
const id = getNextMessageId(baseTimestamp);
|
const id = getNextMessageId(baseTimestamp);
|
||||||
const newItem: HistoryItem = { ...itemData, id } as HistoryItem;
|
const newItem: HistoryItem = { ...itemData, id } as HistoryItem;
|
||||||
|
|
||||||
@@ -63,9 +76,47 @@ export function useHistory(): UseHistoryManagerReturn {
|
|||||||
}
|
}
|
||||||
return [...prevHistory, newItem];
|
return [...prevHistory, newItem];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Record UI-specific messages, but don't do it if we're actually loading
|
||||||
|
// an existing session.
|
||||||
|
if (!isResuming && chatRecordingService) {
|
||||||
|
switch (itemData.type) {
|
||||||
|
case 'compression':
|
||||||
|
case 'info':
|
||||||
|
chatRecordingService?.recordMessage({
|
||||||
|
model: undefined,
|
||||||
|
type: 'info',
|
||||||
|
content: itemData.text ?? '',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'warning':
|
||||||
|
chatRecordingService?.recordMessage({
|
||||||
|
model: undefined,
|
||||||
|
type: 'warning',
|
||||||
|
content: itemData.text ?? '',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
chatRecordingService?.recordMessage({
|
||||||
|
model: undefined,
|
||||||
|
type: 'error',
|
||||||
|
content: itemData.text ?? '',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
case 'gemini':
|
||||||
|
case 'gemini_content':
|
||||||
|
// Core conversation recording handled by GeminiChat.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Ignore the rest.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return id; // Return the generated ID (even if not added, to keep signature)
|
return id; // Return the generated ID (even if not added, to keep signature)
|
||||||
},
|
},
|
||||||
[getNextMessageId],
|
[getNextMessageId, chatRecordingService],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -55,6 +55,47 @@ describe('convertSessionToHistoryFormats', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should convert system, warning, and error messages to appropriate types', () => {
|
||||||
|
const messages: MessageRecord[] = [
|
||||||
|
{
|
||||||
|
id: 'msg-1',
|
||||||
|
timestamp: '2025-01-01T00:01:00Z',
|
||||||
|
content: 'System message',
|
||||||
|
type: 'info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'msg-2',
|
||||||
|
timestamp: '2025-01-01T00:02:00Z',
|
||||||
|
content: 'Warning message',
|
||||||
|
type: 'warning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'msg-3',
|
||||||
|
timestamp: '2025-01-01T00:03:00Z',
|
||||||
|
content: 'Error occurred',
|
||||||
|
type: 'error',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = convertSessionToHistoryFormats(messages);
|
||||||
|
|
||||||
|
expect(result.uiHistory[0]).toEqual({
|
||||||
|
type: MessageType.INFO,
|
||||||
|
text: 'System message',
|
||||||
|
});
|
||||||
|
expect(result.uiHistory[1]).toEqual({
|
||||||
|
type: MessageType.WARNING,
|
||||||
|
text: 'Warning message',
|
||||||
|
});
|
||||||
|
expect(result.uiHistory[2]).toEqual({
|
||||||
|
type: MessageType.ERROR,
|
||||||
|
text: 'Error occurred',
|
||||||
|
});
|
||||||
|
|
||||||
|
// System, warning, and error messages should not be included in client history
|
||||||
|
expect(result.clientHistory).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should filter out slash commands from client history', () => {
|
it('should filter out slash commands from client history', () => {
|
||||||
const messages: MessageRecord[] = [
|
const messages: MessageRecord[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ export function convertSessionToHistoryFormats(
|
|||||||
case 'user':
|
case 'user':
|
||||||
messageType = MessageType.USER;
|
messageType = MessageType.USER;
|
||||||
break;
|
break;
|
||||||
|
case 'info':
|
||||||
|
messageType = MessageType.INFO;
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
messageType = MessageType.ERROR;
|
||||||
|
break;
|
||||||
|
case 'warning':
|
||||||
|
messageType = MessageType.WARNING;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
messageType = MessageType.GEMINI;
|
messageType = MessageType.GEMINI;
|
||||||
break;
|
break;
|
||||||
@@ -70,9 +79,9 @@ export function convertSessionToHistoryFormats(
|
|||||||
|
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
// Skip system/error messages and user slash commands
|
// Skip system/error messages and user slash commands
|
||||||
// if (msg.type === 'system' || msg.type === 'error') {
|
if (msg.type === 'info' || msg.type === 'error' || msg.type === 'warning') {
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (msg.type === 'user') {
|
if (msg.type === 'user') {
|
||||||
// Skip user slash commands
|
// Skip user slash commands
|
||||||
@@ -91,8 +100,7 @@ export function convertSessionToHistoryFormats(
|
|||||||
});
|
});
|
||||||
} else if (msg.type === 'gemini') {
|
} else if (msg.type === 'gemini') {
|
||||||
// Handle Gemini messages with potential tool calls
|
// Handle Gemini messages with potential tool calls
|
||||||
const hasToolCalls =
|
const hasToolCalls = msg.toolCalls && msg.toolCalls.length > 0;
|
||||||
'toolCalls' in msg && msg.toolCalls && msg.toolCalls.length > 0;
|
|
||||||
|
|
||||||
if (hasToolCalls) {
|
if (hasToolCalls) {
|
||||||
// Create model message with function calls
|
// Create model message with function calls
|
||||||
|
|||||||
@@ -101,11 +101,13 @@ describe('useSessionResume', () => {
|
|||||||
1,
|
1,
|
||||||
{ type: 'user', text: 'Hello' },
|
{ type: 'user', text: 'Hello' },
|
||||||
0,
|
0,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(mockHistoryManager.addItem).toHaveBeenNthCalledWith(
|
expect(mockHistoryManager.addItem).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
{ type: 'gemini', text: 'Hi there!' },
|
{ type: 'gemini', text: 'Hi there!' },
|
||||||
1,
|
1,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(mockRefreshStatic).toHaveBeenCalled();
|
expect(mockRefreshStatic).toHaveBeenCalled();
|
||||||
expect(mockGeminiClient.resumeChat).toHaveBeenCalledWith(
|
expect(mockGeminiClient.resumeChat).toHaveBeenCalledWith(
|
||||||
@@ -328,11 +330,13 @@ describe('useSessionResume', () => {
|
|||||||
1,
|
1,
|
||||||
{ type: 'user', text: 'Hello from resumed session' },
|
{ type: 'user', text: 'Hello from resumed session' },
|
||||||
0,
|
0,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(mockHistoryManager.addItem).toHaveBeenNthCalledWith(
|
expect(mockHistoryManager.addItem).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
{ type: 'gemini', text: 'Welcome back!' },
|
{ type: 'gemini', text: 'Welcome back!' },
|
||||||
1,
|
1,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(mockGeminiClient.resumeChat).toHaveBeenCalled();
|
expect(mockGeminiClient.resumeChat).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function useSessionResume({
|
|||||||
setQuittingMessages(null);
|
setQuittingMessages(null);
|
||||||
historyManagerRef.current.clearItems();
|
historyManagerRef.current.clearItems();
|
||||||
uiHistory.forEach((item, index) => {
|
uiHistory.forEach((item, index) => {
|
||||||
historyManagerRef.current.addItem(item, index);
|
historyManagerRef.current.addItem(item, index, true);
|
||||||
});
|
});
|
||||||
refreshStaticRef.current(); // Force Static component to re-render with the updated history.
|
refreshStaticRef.current(); // Force Static component to re-render with the updated history.
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import type {
|
|||||||
LoadableSettingScope,
|
LoadableSettingScope,
|
||||||
LoadedSettings,
|
LoadedSettings,
|
||||||
} from '../../config/settings.js'; // Import LoadedSettings, AppSettings, MergedSetting
|
} from '../../config/settings.js'; // Import LoadedSettings, AppSettings, MergedSetting
|
||||||
import { type HistoryItem, MessageType } from '../types.js';
|
import { MessageType } from '../types.js';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
|
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
|
|
||||||
interface UseThemeCommandReturn {
|
interface UseThemeCommandReturn {
|
||||||
isThemeDialogOpen: boolean;
|
isThemeDialogOpen: boolean;
|
||||||
@@ -24,7 +25,7 @@ interface UseThemeCommandReturn {
|
|||||||
export const useThemeCommand = (
|
export const useThemeCommand = (
|
||||||
loadedSettings: LoadedSettings,
|
loadedSettings: LoadedSettings,
|
||||||
setThemeError: (error: string | null) => void,
|
setThemeError: (error: string | null) => void,
|
||||||
addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
|
addItem: UseHistoryManagerReturn['addItem'],
|
||||||
initialThemeError: string | null,
|
initialThemeError: string | null,
|
||||||
): UseThemeCommandReturn => {
|
): UseThemeCommandReturn => {
|
||||||
const [isThemeDialogOpen, setIsThemeDialogOpen] =
|
const [isThemeDialogOpen, setIsThemeDialogOpen] =
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export interface ToolCallRecord {
|
|||||||
*/
|
*/
|
||||||
export type ConversationRecordExtra =
|
export type ConversationRecordExtra =
|
||||||
| {
|
| {
|
||||||
type: 'user';
|
type: 'user' | 'info' | 'error' | 'warning';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'gemini';
|
type: 'gemini';
|
||||||
|
|||||||
Reference in New Issue
Block a user