@google/gemini-cli 0.19.0-nightly.20251121.5982abeff → 0.19.0-nightly.20251122.42c2e1b21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/google-gemini-cli-0.19.0-nightly.20251120.8e531dc02.tgz +0 -0
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/dist/src/config/auth.js +4 -0
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/auth.test.js +61 -37
- package/dist/src/config/auth.test.js.map +1 -1
- package/dist/src/config/config.integration.test.js +81 -198
- package/dist/src/config/config.integration.test.js.map +1 -1
- package/dist/src/config/config.js +2 -6
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +196 -299
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension.test.js +109 -133
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/consent.test.d.ts +6 -0
- package/dist/src/config/extensions/consent.test.js +152 -0
- package/dist/src/config/extensions/consent.test.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.test.js +82 -15
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.test.js +105 -1
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
- package/dist/src/config/extensions/github.d.ts +1 -0
- package/dist/src/config/extensions/github.js +1 -1
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +197 -318
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/storage.test.d.ts +6 -0
- package/dist/src/config/extensions/storage.test.js +64 -0
- package/dist/src/config/extensions/storage.test.js.map +1 -0
- package/dist/src/config/extensions/update.test.js +154 -263
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/extensions/variables.test.js +87 -1
- package/dist/src/config/extensions/variables.test.js.map +1 -1
- package/dist/src/config/sandboxConfig.d.ts +1 -1
- package/dist/src/config/sandboxConfig.js.map +1 -1
- package/dist/src/config/sandboxConfig.test.d.ts +6 -0
- package/dist/src/config/sandboxConfig.test.js +178 -0
- package/dist/src/config/sandboxConfig.test.js.map +1 -0
- package/dist/src/config/settingPaths.test.d.ts +6 -0
- package/dist/src/config/settingPaths.test.js +22 -0
- package/dist/src/config/settingPaths.test.js.map +1 -0
- package/dist/src/config/settings.test.js +164 -226
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +9 -9
- package/dist/src/config/settingsSchema.js +9 -9
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +0 -6
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/gemini.d.ts +1 -1
- package/dist/src/gemini.js +5 -6
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +4 -0
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/services/BuiltinCommandLoader.js +1 -1
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +0 -22
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +1 -1
- package/dist/src/ui/AppContainer.js +13 -11
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +3 -6
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.js +17 -10
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.test.js +5 -2
- package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.js +1 -1
- package/dist/src/ui/components/DebugProfiler.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +6 -1
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +1 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +6 -1
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +1 -1
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/SessionBrowser.d.ts +98 -0
- package/dist/src/ui/components/SessionBrowser.js +457 -0
- package/dist/src/ui/components/SessionBrowser.js.map +1 -0
- package/dist/src/ui/components/SessionBrowser.test.d.ts +6 -0
- package/dist/src/ui/components/SessionBrowser.test.js +245 -0
- package/dist/src/ui/components/SessionBrowser.test.js.map +1 -0
- package/dist/src/ui/components/messages/ShellToolMessage.js +2 -2
- package/dist/src/ui/components/messages/ShellToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.d.ts +5 -0
- package/dist/src/ui/components/messages/ToolMessage.js +32 -3
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +1 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -0
- package/dist/src/ui/hooks/shellCommandProcessor.js +3 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useBracketedPaste.js +3 -5
- package/dist/src/ui/hooks/useBracketedPaste.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.d.ts +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +6 -4
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useInactivityTimer.d.ts +14 -0
- package/dist/src/ui/hooks/useInactivityTimer.js +30 -0
- package/dist/src/ui/hooks/useInactivityTimer.js.map +1 -0
- package/dist/src/ui/hooks/useLoadingIndicator.d.ts +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.js +2 -2
- package/dist/src/ui/hooks/useLoadingIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +16 -5
- package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.d.ts +4 -1
- package/dist/src/ui/hooks/usePhraseCycler.js +52 -43
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.test.js +52 -3
- package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.d.ts +2 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +3 -0
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.d.ts +18 -1
- package/dist/src/ui/hooks/useSessionBrowser.js +59 -0
- package/dist/src/ui/hooks/useSessionBrowser.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.test.js +154 -526
- package/dist/src/ui/hooks/useSessionBrowser.test.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js +121 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.js +34 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.js +34 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.js +62 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/types.js +1 -1
- package/dist/src/ui/utils/bracketedPaste.d.ts +7 -0
- package/dist/src/ui/utils/bracketedPaste.js +15 -0
- package/dist/src/ui/utils/bracketedPaste.js.map +1 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +3 -4
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
- package/dist/src/ui/utils/mouse.d.ts +2 -2
- package/dist/src/ui/utils/mouse.js +2 -11
- package/dist/src/ui/utils/mouse.js.map +1 -1
- package/dist/src/utils/sessionCleanup.test.js +38 -0
- package/dist/src/utils/sessionCleanup.test.js.map +1 -1
- package/dist/src/utils/sessionUtils.d.ts +49 -4
- package/dist/src/utils/sessionUtils.js +100 -25
- package/dist/src/utils/sessionUtils.js.map +1 -1
- package/dist/src/utils/sessionUtils.test.js +46 -3
- package/dist/src/utils/sessionUtils.test.js.map +1 -1
- package/dist/src/utils/sessions.js +4 -1
- package/dist/src/utils/sessions.js.map +1 -1
- package/dist/src/utils/sessions.test.js +42 -0
- package/dist/src/utils/sessions.test.js.map +1 -1
- package/dist/src/zed-integration/connection.test.d.ts +6 -0
- package/dist/src/zed-integration/connection.test.js +175 -0
- package/dist/src/zed-integration/connection.test.js.map +1 -0
- package/dist/src/zed-integration/fileSystemService.test.d.ts +6 -0
- package/dist/src/zed-integration/fileSystemService.test.js +98 -0
- package/dist/src/zed-integration/fileSystemService.test.js.map +1 -0
- package/dist/src/zed-integration/zedIntegration.d.ts +31 -1
- package/dist/src/zed-integration/zedIntegration.js +5 -2
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.test.d.ts +6 -0
- package/dist/src/zed-integration/zedIntegration.test.js +619 -0
- package/dist/src/zed-integration/zedIntegration.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -3,38 +3,120 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { renderHook } from '../../test-utils/render.js';
|
|
8
|
+
import { act } from 'react';
|
|
9
|
+
import { useSessionBrowser, convertSessionToHistoryFormats, } from './useSessionBrowser.js';
|
|
10
|
+
import * as fs from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { getSessionFiles } from '../../utils/sessionUtils.js';
|
|
13
|
+
// Mock modules
|
|
14
|
+
vi.mock('fs/promises');
|
|
15
|
+
vi.mock('path');
|
|
16
|
+
vi.mock('../../utils/sessionUtils.js');
|
|
17
|
+
const MOCKED_PROJECT_TEMP_DIR = '/test/project/temp';
|
|
18
|
+
const MOCKED_CHATS_DIR = '/test/project/temp/chats';
|
|
19
|
+
const MOCKED_SESSION_ID = 'test-session-123';
|
|
20
|
+
const MOCKED_CURRENT_SESSION_ID = 'current-session-id';
|
|
21
|
+
describe('useSessionBrowser', () => {
|
|
22
|
+
const mockedFs = vi.mocked(fs);
|
|
23
|
+
const mockedPath = vi.mocked(path);
|
|
24
|
+
const mockedGetSessionFiles = vi.mocked(getSessionFiles);
|
|
25
|
+
const mockConfig = {
|
|
26
|
+
storage: {
|
|
27
|
+
getProjectTempDir: vi.fn(),
|
|
28
|
+
},
|
|
29
|
+
setSessionId: vi.fn(),
|
|
30
|
+
getSessionId: vi.fn(),
|
|
31
|
+
getGeminiClient: vi.fn().mockReturnValue({
|
|
32
|
+
getChatRecordingService: vi.fn().mockReturnValue({
|
|
33
|
+
deleteSession: vi.fn(),
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
const mockOnLoadHistory = vi.fn();
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.resetAllMocks();
|
|
40
|
+
mockedPath.join.mockImplementation((...args) => args.join('/'));
|
|
41
|
+
vi.mocked(mockConfig.storage.getProjectTempDir).mockReturnValue(MOCKED_PROJECT_TEMP_DIR);
|
|
42
|
+
vi.mocked(mockConfig.getSessionId).mockReturnValue(MOCKED_CURRENT_SESSION_ID);
|
|
43
|
+
});
|
|
44
|
+
it('should successfully resume a session', async () => {
|
|
45
|
+
const MOCKED_FILENAME = 'session-2025-01-01-test-session-123.json';
|
|
46
|
+
const mockConversation = {
|
|
47
|
+
sessionId: 'existing-session-456',
|
|
48
|
+
messages: [{ type: 'user', content: 'Hello' }],
|
|
49
|
+
};
|
|
50
|
+
const mockSession = {
|
|
51
|
+
id: MOCKED_SESSION_ID,
|
|
52
|
+
fileName: MOCKED_FILENAME,
|
|
53
|
+
};
|
|
54
|
+
mockedGetSessionFiles.mockResolvedValue([mockSession]);
|
|
55
|
+
mockedFs.readFile.mockResolvedValue(JSON.stringify(mockConversation));
|
|
56
|
+
const { result } = renderHook(() => useSessionBrowser(mockConfig, mockOnLoadHistory));
|
|
57
|
+
await act(async () => {
|
|
58
|
+
await result.current.handleResumeSession(mockSession);
|
|
59
|
+
});
|
|
60
|
+
expect(mockedFs.readFile).toHaveBeenCalledWith(`${MOCKED_CHATS_DIR}/${MOCKED_FILENAME}`, 'utf8');
|
|
61
|
+
expect(mockConfig.setSessionId).toHaveBeenCalledWith('existing-session-456');
|
|
62
|
+
expect(result.current.isSessionBrowserOpen).toBe(false);
|
|
63
|
+
expect(mockOnLoadHistory).toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
it('should handle file read error', async () => {
|
|
66
|
+
const MOCKED_FILENAME = 'session-2025-01-01-test-session-123.json';
|
|
67
|
+
const mockSession = {
|
|
68
|
+
id: MOCKED_SESSION_ID,
|
|
69
|
+
fileName: MOCKED_FILENAME,
|
|
70
|
+
};
|
|
71
|
+
mockedFs.readFile.mockRejectedValue(new Error('File not found'));
|
|
72
|
+
const consoleErrorSpy = vi
|
|
73
|
+
.spyOn(console, 'error')
|
|
74
|
+
.mockImplementation(() => { });
|
|
75
|
+
const { result } = renderHook(() => useSessionBrowser(mockConfig, mockOnLoadHistory));
|
|
76
|
+
await act(async () => {
|
|
77
|
+
await result.current.handleResumeSession(mockSession);
|
|
78
|
+
});
|
|
79
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
80
|
+
expect(result.current.isSessionBrowserOpen).toBe(false);
|
|
81
|
+
consoleErrorSpy.mockRestore();
|
|
82
|
+
});
|
|
83
|
+
it('should handle JSON parse error', async () => {
|
|
84
|
+
const MOCKED_FILENAME = 'invalid.json';
|
|
85
|
+
const mockSession = {
|
|
86
|
+
id: MOCKED_SESSION_ID,
|
|
87
|
+
fileName: MOCKED_FILENAME,
|
|
88
|
+
};
|
|
89
|
+
mockedFs.readFile.mockResolvedValue('invalid json');
|
|
90
|
+
const consoleErrorSpy = vi
|
|
91
|
+
.spyOn(console, 'error')
|
|
92
|
+
.mockImplementation(() => { });
|
|
93
|
+
const { result } = renderHook(() => useSessionBrowser(mockConfig, mockOnLoadHistory));
|
|
94
|
+
await act(async () => {
|
|
95
|
+
await result.current.handleResumeSession(mockSession);
|
|
96
|
+
});
|
|
97
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
98
|
+
expect(result.current.isSessionBrowserOpen).toBe(false);
|
|
99
|
+
consoleErrorSpy.mockRestore();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// The convertSessionToHistoryFormats tests are self-contained and do not need changes.
|
|
8
103
|
describe('convertSessionToHistoryFormats', () => {
|
|
9
104
|
it('should convert empty messages array', () => {
|
|
10
105
|
const result = convertSessionToHistoryFormats([]);
|
|
11
106
|
expect(result.uiHistory).toEqual([]);
|
|
12
107
|
expect(result.clientHistory).toEqual([]);
|
|
13
108
|
});
|
|
14
|
-
it('should convert basic user and
|
|
109
|
+
it('should convert basic user and model messages', () => {
|
|
15
110
|
const messages = [
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
19
|
-
content: 'Hello',
|
|
20
|
-
type: 'user',
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
id: 'msg-2',
|
|
24
|
-
timestamp: '2025-01-01T00:02:00Z',
|
|
25
|
-
content: 'Hi there!',
|
|
26
|
-
type: 'gemini',
|
|
27
|
-
},
|
|
111
|
+
{ type: 'user', content: 'Hello' },
|
|
112
|
+
{ type: 'gemini', content: 'Hi there' },
|
|
28
113
|
];
|
|
29
114
|
const result = convertSessionToHistoryFormats(messages);
|
|
30
115
|
expect(result.uiHistory).toHaveLength(2);
|
|
31
|
-
expect(result.uiHistory[0]).
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expect(result.uiHistory[1]).toEqual({
|
|
36
|
-
type: MessageType.GEMINI,
|
|
37
|
-
text: 'Hi there!',
|
|
116
|
+
expect(result.uiHistory[0]).toMatchObject({ type: 'user', text: 'Hello' });
|
|
117
|
+
expect(result.uiHistory[1]).toMatchObject({
|
|
118
|
+
type: 'gemini',
|
|
119
|
+
text: 'Hi there',
|
|
38
120
|
});
|
|
39
121
|
expect(result.clientHistory).toHaveLength(2);
|
|
40
122
|
expect(result.clientHistory[0]).toEqual({
|
|
@@ -43,538 +125,84 @@ describe('convertSessionToHistoryFormats', () => {
|
|
|
43
125
|
});
|
|
44
126
|
expect(result.clientHistory[1]).toEqual({
|
|
45
127
|
role: 'model',
|
|
46
|
-
parts: [{ text: 'Hi there
|
|
128
|
+
parts: [{ text: 'Hi there' }],
|
|
47
129
|
});
|
|
48
130
|
});
|
|
49
|
-
it('should
|
|
131
|
+
it('should filter out slash commands from client history but keep in UI', () => {
|
|
50
132
|
const messages = [
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
54
|
-
content: 'System message',
|
|
55
|
-
type: 'info',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
id: 'msg-2',
|
|
59
|
-
timestamp: '2025-01-01T00:02:00Z',
|
|
60
|
-
content: 'Warning message',
|
|
61
|
-
type: 'warning',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: 'msg-3',
|
|
65
|
-
timestamp: '2025-01-01T00:03:00Z',
|
|
66
|
-
content: 'Error occurred',
|
|
67
|
-
type: 'error',
|
|
68
|
-
},
|
|
133
|
+
{ type: 'user', content: '/help' },
|
|
134
|
+
{ type: 'info', content: 'Help text' },
|
|
69
135
|
];
|
|
70
136
|
const result = convertSessionToHistoryFormats(messages);
|
|
71
|
-
expect(result.uiHistory
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type: MessageType.WARNING,
|
|
77
|
-
text: 'Warning message',
|
|
78
|
-
});
|
|
79
|
-
expect(result.uiHistory[2]).toEqual({
|
|
80
|
-
type: MessageType.ERROR,
|
|
81
|
-
text: 'Error occurred',
|
|
82
|
-
});
|
|
83
|
-
// System, warning, and error messages should not be included in client history
|
|
84
|
-
expect(result.clientHistory).toEqual([]);
|
|
85
|
-
});
|
|
86
|
-
it('should filter out slash commands from client history', () => {
|
|
87
|
-
const messages = [
|
|
88
|
-
{
|
|
89
|
-
id: 'msg-1',
|
|
90
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
91
|
-
content: '/help',
|
|
92
|
-
type: 'user',
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: 'msg-2',
|
|
96
|
-
timestamp: '2025-01-01T00:02:00Z',
|
|
97
|
-
content: '?quit',
|
|
98
|
-
type: 'user',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
id: 'msg-3',
|
|
102
|
-
timestamp: '2025-01-01T00:03:00Z',
|
|
103
|
-
content: 'Regular message',
|
|
104
|
-
type: 'user',
|
|
105
|
-
},
|
|
106
|
-
];
|
|
107
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
108
|
-
// All messages should appear in UI history
|
|
109
|
-
expect(result.uiHistory).toHaveLength(3);
|
|
110
|
-
// Only non-slash commands should appear in client history
|
|
111
|
-
expect(result.clientHistory).toHaveLength(1);
|
|
112
|
-
expect(result.clientHistory[0]).toEqual({
|
|
113
|
-
role: 'user',
|
|
114
|
-
parts: [{ text: 'Regular message' }],
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
it('should handle tool calls correctly', () => {
|
|
118
|
-
const messages = [
|
|
119
|
-
{
|
|
120
|
-
id: 'msg-1',
|
|
121
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
122
|
-
content: "I'll help you with that.",
|
|
123
|
-
type: 'gemini',
|
|
124
|
-
toolCalls: [
|
|
125
|
-
{
|
|
126
|
-
id: 'tool-1',
|
|
127
|
-
name: 'bash',
|
|
128
|
-
displayName: 'Execute Command',
|
|
129
|
-
description: 'Run bash command',
|
|
130
|
-
args: { command: 'ls -la' },
|
|
131
|
-
status: 'success',
|
|
132
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
133
|
-
resultDisplay: 'total 4\ndrwxr-xr-x 2 user user 4096 Jan 1 00:00 .',
|
|
134
|
-
renderOutputAsMarkdown: false,
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
id: 'tool-2',
|
|
138
|
-
name: 'read',
|
|
139
|
-
displayName: 'Read File',
|
|
140
|
-
description: 'Read file contents',
|
|
141
|
-
args: { path: '/etc/hosts' },
|
|
142
|
-
status: 'error',
|
|
143
|
-
timestamp: '2025-01-01T00:01:45Z',
|
|
144
|
-
resultDisplay: 'Permission denied',
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
];
|
|
149
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
150
|
-
expect(result.uiHistory).toHaveLength(2); // text message + tool group
|
|
151
|
-
expect(result.uiHistory[0]).toEqual({
|
|
152
|
-
type: MessageType.GEMINI,
|
|
153
|
-
text: "I'll help you with that.",
|
|
154
|
-
});
|
|
155
|
-
expect(result.uiHistory[1].type).toBe('tool_group');
|
|
156
|
-
// This if-statement is only necessary because TypeScript can't tell that the toBe() assertion
|
|
157
|
-
// protects the .tools access below.
|
|
158
|
-
if (result.uiHistory[1].type === 'tool_group') {
|
|
159
|
-
expect(result.uiHistory[1].tools).toHaveLength(2);
|
|
160
|
-
expect(result.uiHistory[1].tools[0]).toEqual({
|
|
161
|
-
callId: 'tool-1',
|
|
162
|
-
name: 'Execute Command',
|
|
163
|
-
description: 'Run bash command',
|
|
164
|
-
renderOutputAsMarkdown: false,
|
|
165
|
-
status: ToolCallStatus.Success,
|
|
166
|
-
resultDisplay: 'total 4\ndrwxr-xr-x 2 user user 4096 Jan 1 00:00 .',
|
|
167
|
-
confirmationDetails: undefined,
|
|
168
|
-
});
|
|
169
|
-
expect(result.uiHistory[1].tools[1]).toEqual({
|
|
170
|
-
callId: 'tool-2',
|
|
171
|
-
name: 'Read File',
|
|
172
|
-
description: 'Read file contents',
|
|
173
|
-
renderOutputAsMarkdown: true, // default value
|
|
174
|
-
status: ToolCallStatus.Error,
|
|
175
|
-
resultDisplay: 'Permission denied',
|
|
176
|
-
confirmationDetails: undefined,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
it('should skip empty tool calls arrays', () => {
|
|
181
|
-
const messages = [
|
|
182
|
-
{
|
|
183
|
-
id: 'msg-1',
|
|
184
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
185
|
-
content: 'Message with empty tools',
|
|
186
|
-
type: 'gemini',
|
|
187
|
-
toolCalls: [],
|
|
188
|
-
},
|
|
189
|
-
];
|
|
190
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
191
|
-
expect(result.uiHistory).toHaveLength(1); // Only text message
|
|
192
|
-
expect(result.uiHistory[0]).toEqual({
|
|
193
|
-
type: MessageType.GEMINI,
|
|
194
|
-
text: 'Message with empty tools',
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
it('should not add tool calls for user messages', () => {
|
|
198
|
-
const messages = [
|
|
199
|
-
{
|
|
200
|
-
id: 'msg-1',
|
|
201
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
202
|
-
content: 'User message',
|
|
203
|
-
type: 'user',
|
|
204
|
-
// This would be invalid in real usage, but testing robustness
|
|
205
|
-
toolCalls: [
|
|
206
|
-
{
|
|
207
|
-
id: 'tool-1',
|
|
208
|
-
name: 'invalid',
|
|
209
|
-
args: {},
|
|
210
|
-
status: 'success',
|
|
211
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
},
|
|
215
|
-
];
|
|
216
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
217
|
-
expect(result.uiHistory).toHaveLength(1); // Only user message, no tool group
|
|
218
|
-
expect(result.uiHistory[0]).toEqual({
|
|
219
|
-
type: MessageType.USER,
|
|
220
|
-
text: 'User message',
|
|
137
|
+
expect(result.uiHistory).toHaveLength(2);
|
|
138
|
+
expect(result.uiHistory[0]).toMatchObject({ type: 'user', text: '/help' });
|
|
139
|
+
expect(result.uiHistory[1]).toMatchObject({
|
|
140
|
+
type: 'info',
|
|
141
|
+
text: 'Help text',
|
|
221
142
|
});
|
|
143
|
+
expect(result.clientHistory).toHaveLength(0);
|
|
222
144
|
});
|
|
223
|
-
it('should handle
|
|
145
|
+
it('should handle tool calls and responses', () => {
|
|
224
146
|
const messages = [
|
|
147
|
+
{ type: 'user', content: 'What time is it?' },
|
|
225
148
|
{
|
|
226
|
-
id: 'msg-1',
|
|
227
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
228
|
-
content: 'Message with minimal tool',
|
|
229
149
|
type: 'gemini',
|
|
150
|
+
content: '',
|
|
230
151
|
toolCalls: [
|
|
231
152
|
{
|
|
232
|
-
id: '
|
|
233
|
-
name: '
|
|
153
|
+
id: 'call_1',
|
|
154
|
+
name: 'get_time',
|
|
234
155
|
args: {},
|
|
235
156
|
status: 'success',
|
|
236
|
-
|
|
237
|
-
// Missing optional fields
|
|
157
|
+
result: '12:00',
|
|
238
158
|
},
|
|
239
159
|
],
|
|
240
160
|
},
|
|
241
161
|
];
|
|
242
162
|
const result = convertSessionToHistoryFormats(messages);
|
|
243
163
|
expect(result.uiHistory).toHaveLength(2);
|
|
244
|
-
expect(result.uiHistory[
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const messages = [
|
|
263
|
-
{
|
|
264
|
-
id: 'msg-1',
|
|
265
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
266
|
-
content: 'List files',
|
|
267
|
-
type: 'user',
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
id: 'msg-2',
|
|
271
|
-
timestamp: '2025-01-01T00:02:00Z',
|
|
272
|
-
content: "I'll list the files for you.",
|
|
273
|
-
type: 'gemini',
|
|
274
|
-
toolCalls: [
|
|
275
|
-
{
|
|
276
|
-
id: 'tool-1',
|
|
277
|
-
name: 'list_directory',
|
|
278
|
-
args: { path: '/home/user' },
|
|
279
|
-
result: {
|
|
280
|
-
functionResponse: {
|
|
281
|
-
id: 'list_directory-1753650620141-f3b8b9e73919d',
|
|
282
|
-
name: 'list_directory',
|
|
283
|
-
response: {
|
|
284
|
-
output: 'file1.txt\nfile2.txt',
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
status: 'success',
|
|
289
|
-
timestamp: '2025-01-01T00:02:30Z',
|
|
290
|
-
},
|
|
291
|
-
],
|
|
292
|
-
},
|
|
293
|
-
];
|
|
294
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
295
|
-
// Should have: user message, model with function call, user with function response
|
|
296
|
-
expect(result.clientHistory).toHaveLength(3);
|
|
297
|
-
// User message
|
|
298
|
-
expect(result.clientHistory[0]).toEqual({
|
|
299
|
-
role: 'user',
|
|
300
|
-
parts: [{ text: 'List files' }],
|
|
301
|
-
});
|
|
302
|
-
// Model message with function call
|
|
303
|
-
expect(result.clientHistory[1]).toEqual({
|
|
304
|
-
role: 'model',
|
|
305
|
-
parts: [
|
|
306
|
-
{ text: "I'll list the files for you." },
|
|
307
|
-
{
|
|
308
|
-
functionCall: {
|
|
309
|
-
name: 'list_directory',
|
|
310
|
-
args: { path: '/home/user' },
|
|
311
|
-
id: 'tool-1',
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
});
|
|
316
|
-
// Function response
|
|
317
|
-
expect(result.clientHistory[2]).toEqual({
|
|
318
|
-
role: 'user',
|
|
319
|
-
parts: [
|
|
320
|
-
{
|
|
321
|
-
functionResponse: {
|
|
322
|
-
id: 'list_directory-1753650620141-f3b8b9e73919d',
|
|
323
|
-
name: 'list_directory',
|
|
324
|
-
response: { output: 'file1.txt\nfile2.txt' },
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
});
|
|
164
|
+
expect(result.uiHistory[0]).toMatchObject({
|
|
165
|
+
type: 'user',
|
|
166
|
+
text: 'What time is it?',
|
|
167
|
+
});
|
|
168
|
+
expect(result.uiHistory[1]).toMatchObject({
|
|
169
|
+
type: 'tool_group',
|
|
170
|
+
tools: [
|
|
171
|
+
expect.objectContaining({
|
|
172
|
+
callId: 'call_1',
|
|
173
|
+
name: 'get_time',
|
|
174
|
+
status: 'Success',
|
|
175
|
+
}),
|
|
176
|
+
],
|
|
177
|
+
});
|
|
178
|
+
expect(result.clientHistory).toHaveLength(3); // User, Model (call), User (response)
|
|
179
|
+
expect(result.clientHistory[0]).toEqual({
|
|
180
|
+
role: 'user',
|
|
181
|
+
parts: [{ text: 'What time is it?' }],
|
|
329
182
|
});
|
|
330
|
-
|
|
331
|
-
|
|
183
|
+
expect(result.clientHistory[1]).toEqual({
|
|
184
|
+
role: 'model',
|
|
185
|
+
parts: [
|
|
332
186
|
{
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
toolCalls: [
|
|
338
|
-
{
|
|
339
|
-
id: 'tool-1',
|
|
340
|
-
name: 'bash',
|
|
341
|
-
args: { command: 'ls' },
|
|
342
|
-
result: 'file1.txt\nfile2.txt',
|
|
343
|
-
status: 'success',
|
|
344
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
];
|
|
349
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
350
|
-
expect(result.clientHistory).toHaveLength(2);
|
|
351
|
-
// Model message with only function call (no text)
|
|
352
|
-
expect(result.clientHistory[0]).toEqual({
|
|
353
|
-
role: 'model',
|
|
354
|
-
parts: [
|
|
355
|
-
{
|
|
356
|
-
functionCall: {
|
|
357
|
-
name: 'bash',
|
|
358
|
-
args: { command: 'ls' },
|
|
359
|
-
id: 'tool-1',
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
],
|
|
363
|
-
});
|
|
364
|
-
// Function response
|
|
365
|
-
expect(result.clientHistory[1]).toEqual({
|
|
366
|
-
role: 'user',
|
|
367
|
-
parts: [
|
|
368
|
-
{
|
|
369
|
-
functionResponse: {
|
|
370
|
-
id: 'tool-1',
|
|
371
|
-
name: 'bash',
|
|
372
|
-
response: {
|
|
373
|
-
output: 'file1.txt\nfile2.txt',
|
|
374
|
-
},
|
|
375
|
-
},
|
|
187
|
+
functionCall: {
|
|
188
|
+
name: 'get_time',
|
|
189
|
+
args: {},
|
|
190
|
+
id: 'call_1',
|
|
376
191
|
},
|
|
377
|
-
],
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
it('should handle multiple tool calls in one message', () => {
|
|
381
|
-
const messages = [
|
|
382
|
-
{
|
|
383
|
-
id: 'msg-1',
|
|
384
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
385
|
-
content: 'Running multiple commands',
|
|
386
|
-
type: 'gemini',
|
|
387
|
-
toolCalls: [
|
|
388
|
-
{
|
|
389
|
-
id: 'tool-1',
|
|
390
|
-
name: 'bash',
|
|
391
|
-
args: { command: 'pwd' },
|
|
392
|
-
result: '/home/user',
|
|
393
|
-
status: 'success',
|
|
394
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
id: 'tool-2',
|
|
398
|
-
name: 'bash',
|
|
399
|
-
args: { command: 'ls' },
|
|
400
|
-
result: [
|
|
401
|
-
{
|
|
402
|
-
functionResponse: {
|
|
403
|
-
id: 'tool-2',
|
|
404
|
-
name: 'bash',
|
|
405
|
-
response: {
|
|
406
|
-
output: 'file1.txt',
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
functionResponse: {
|
|
412
|
-
id: 'tool-2',
|
|
413
|
-
name: 'bash',
|
|
414
|
-
response: {
|
|
415
|
-
output: 'file2.txt',
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
],
|
|
420
|
-
status: 'success',
|
|
421
|
-
timestamp: '2025-01-01T00:01:35Z',
|
|
422
|
-
},
|
|
423
|
-
],
|
|
424
192
|
},
|
|
425
|
-
]
|
|
426
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
427
|
-
// Should have: model with both function calls, then one response
|
|
428
|
-
expect(result.clientHistory).toHaveLength(2);
|
|
429
|
-
// Model message with both function calls
|
|
430
|
-
expect(result.clientHistory[0]).toEqual({
|
|
431
|
-
role: 'model',
|
|
432
|
-
parts: [
|
|
433
|
-
{ text: 'Running multiple commands' },
|
|
434
|
-
{
|
|
435
|
-
functionCall: {
|
|
436
|
-
name: 'bash',
|
|
437
|
-
args: { command: 'pwd' },
|
|
438
|
-
id: 'tool-1',
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
functionCall: {
|
|
443
|
-
name: 'bash',
|
|
444
|
-
args: { command: 'ls' },
|
|
445
|
-
id: 'tool-2',
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
],
|
|
449
|
-
});
|
|
450
|
-
// First function response
|
|
451
|
-
expect(result.clientHistory[1]).toEqual({
|
|
452
|
-
role: 'user',
|
|
453
|
-
parts: [
|
|
454
|
-
{
|
|
455
|
-
functionResponse: {
|
|
456
|
-
id: 'tool-1',
|
|
457
|
-
name: 'bash',
|
|
458
|
-
response: { output: '/home/user' },
|
|
459
|
-
},
|
|
460
|
-
},
|
|
461
|
-
{
|
|
462
|
-
functionResponse: {
|
|
463
|
-
id: 'tool-2',
|
|
464
|
-
name: 'bash',
|
|
465
|
-
response: { output: 'file1.txt' },
|
|
466
|
-
},
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
functionResponse: {
|
|
470
|
-
id: 'tool-2',
|
|
471
|
-
name: 'bash',
|
|
472
|
-
response: { output: 'file2.txt' },
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
],
|
|
476
|
-
});
|
|
193
|
+
],
|
|
477
194
|
});
|
|
478
|
-
|
|
479
|
-
|
|
195
|
+
expect(result.clientHistory[2]).toEqual({
|
|
196
|
+
role: 'user',
|
|
197
|
+
parts: [
|
|
480
198
|
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
toolCalls: [
|
|
486
|
-
{
|
|
487
|
-
id: 'tool-1',
|
|
488
|
-
name: 'read_file',
|
|
489
|
-
args: { path: 'test.txt' },
|
|
490
|
-
result: [
|
|
491
|
-
{
|
|
492
|
-
functionResponse: {
|
|
493
|
-
id: 'tool-1',
|
|
494
|
-
name: 'read_file',
|
|
495
|
-
response: {
|
|
496
|
-
output: 'Hello',
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
},
|
|
500
|
-
{
|
|
501
|
-
functionResponse: {
|
|
502
|
-
id: 'tool-1',
|
|
503
|
-
name: 'read_file',
|
|
504
|
-
response: {
|
|
505
|
-
output: ' World',
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
],
|
|
510
|
-
status: 'success',
|
|
511
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
512
|
-
},
|
|
513
|
-
],
|
|
514
|
-
},
|
|
515
|
-
];
|
|
516
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
517
|
-
expect(result.clientHistory).toHaveLength(2);
|
|
518
|
-
// Function response should extract both function responses
|
|
519
|
-
expect(result.clientHistory[1]).toEqual({
|
|
520
|
-
role: 'user',
|
|
521
|
-
parts: [
|
|
522
|
-
{
|
|
523
|
-
functionResponse: {
|
|
524
|
-
id: 'tool-1',
|
|
525
|
-
name: 'read_file',
|
|
526
|
-
response: {
|
|
527
|
-
output: 'Hello',
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
functionResponse: {
|
|
533
|
-
id: 'tool-1',
|
|
534
|
-
name: 'read_file',
|
|
535
|
-
response: {
|
|
536
|
-
output: ' World',
|
|
537
|
-
},
|
|
538
|
-
},
|
|
199
|
+
functionResponse: {
|
|
200
|
+
id: 'call_1',
|
|
201
|
+
name: 'get_time',
|
|
202
|
+
response: { output: '12:00' },
|
|
539
203
|
},
|
|
540
|
-
],
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
it('should skip tool calls without results', () => {
|
|
544
|
-
const messages = [
|
|
545
|
-
{
|
|
546
|
-
id: 'msg-1',
|
|
547
|
-
timestamp: '2025-01-01T00:01:00Z',
|
|
548
|
-
content: 'Testing tool',
|
|
549
|
-
type: 'gemini',
|
|
550
|
-
toolCalls: [
|
|
551
|
-
{
|
|
552
|
-
id: 'tool-1',
|
|
553
|
-
name: 'test_tool',
|
|
554
|
-
args: { arg: 'value' },
|
|
555
|
-
// No result field
|
|
556
|
-
status: 'error',
|
|
557
|
-
timestamp: '2025-01-01T00:01:30Z',
|
|
558
|
-
},
|
|
559
|
-
],
|
|
560
204
|
},
|
|
561
|
-
]
|
|
562
|
-
const result = convertSessionToHistoryFormats(messages);
|
|
563
|
-
// Should only have the model message with function call, no function response
|
|
564
|
-
expect(result.clientHistory).toHaveLength(1);
|
|
565
|
-
expect(result.clientHistory[0]).toEqual({
|
|
566
|
-
role: 'model',
|
|
567
|
-
parts: [
|
|
568
|
-
{ text: 'Testing tool' },
|
|
569
|
-
{
|
|
570
|
-
functionCall: {
|
|
571
|
-
name: 'test_tool',
|
|
572
|
-
args: { arg: 'value' },
|
|
573
|
-
id: 'tool-1',
|
|
574
|
-
},
|
|
575
|
-
},
|
|
576
|
-
],
|
|
577
|
-
});
|
|
205
|
+
],
|
|
578
206
|
});
|
|
579
207
|
});
|
|
580
208
|
});
|