@google/gemini-cli-core 0.21.0-nightly.20251210.d90356e8a → 0.21.0-nightly.20251211.8c83e1ea9
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-core-0.21.0-nightly.20251209.ec9a8c7a7.tgz +0 -0
- package/dist/src/agents/delegate-to-agent-tool.d.ts +19 -0
- package/dist/src/agents/delegate-to-agent-tool.js +111 -0
- package/dist/src/agents/delegate-to-agent-tool.js.map +1 -0
- package/dist/src/agents/delegate-to-agent-tool.test.d.ts +6 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js +133 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -0
- package/dist/src/agents/registry.d.ts +15 -0
- package/dist/src/agents/registry.js +39 -0
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +18 -0
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/availability/modelAvailabilityService.d.ts +1 -0
- package/dist/src/availability/modelAvailabilityService.js +3 -0
- package/dist/src/availability/modelAvailabilityService.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.js +2 -1
- package/dist/src/code_assist/experiments/client_metadata.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.test.js +7 -10
- package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -1
- package/dist/src/config/config.js +28 -17
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +49 -8
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +10 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/core/contentGenerator.js +4 -2
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +29 -2
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/prompts.js +8 -6
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +9 -3
- package/dist/src/core/prompts.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/hooks/hookEventHandler.js +6 -1
- package/dist/src/hooks/hookEventHandler.js.map +1 -1
- package/dist/src/hooks/hookEventHandler.test.js +8 -1
- package/dist/src/hooks/hookEventHandler.test.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/policy/policies/agent.toml +31 -0
- package/dist/src/services/chatRecordingService.d.ts +5 -0
- package/dist/src/services/chatRecordingService.js +7 -0
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/fileSystemService.d.ts +0 -9
- package/dist/src/services/fileSystemService.js +0 -11
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/modelConfig.golden.test.js +32 -0
- package/dist/src/services/modelConfig.golden.test.js.map +1 -1
- package/dist/src/services/modelConfigService.d.ts +3 -0
- package/dist/src/services/modelConfigService.js +3 -2
- package/dist/src/services/modelConfigService.js.map +1 -1
- package/dist/src/services/modelConfigService.test.js +110 -0
- package/dist/src/services/modelConfigService.test.js.map +1 -1
- package/dist/src/services/sessionSummaryUtils.d.ts +8 -3
- package/dist/src/services/sessionSummaryUtils.js +106 -38
- package/dist/src/services/sessionSummaryUtils.js.map +1 -1
- package/dist/src/services/sessionSummaryUtils.test.js +83 -244
- package/dist/src/services/sessionSummaryUtils.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases-retry.golden.json +222 -0
- package/dist/src/tools/tool-names.d.ts +1 -0
- package/dist/src/tools/tool-names.js +1 -0
- package/dist/src/tools/tool-names.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.d.ts +8 -0
- package/dist/src/utils/bfsFileSearch.js +63 -23
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +65 -1
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/googleErrors.js +31 -18
- package/dist/src/utils/googleErrors.js.map +1 -1
- package/dist/src/utils/googleErrors.test.js +10 -2
- package/dist/src/utils/googleErrors.test.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.js +6 -3
- package/dist/src/utils/googleQuotaErrors.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.test.js +41 -0
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -1
- package/dist/src/utils/pathCorrector.js +12 -2
- package/dist/src/utils/pathCorrector.js.map +1 -1
- package/dist/src/utils/pathCorrector.test.js +6 -2
- package/dist/src/utils/pathCorrector.test.js.map +1 -1
- package/dist/src/utils/version.d.ts +6 -0
- package/dist/src/utils/version.js +15 -0
- package/dist/src/utils/version.js.map +1 -0
- package/dist/src/utils/version.test.d.ts +6 -0
- package/dist/src/utils/version.test.js +39 -0
- package/dist/src/utils/version.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -6,56 +6,124 @@
|
|
|
6
6
|
import { SessionSummaryService } from './sessionSummaryService.js';
|
|
7
7
|
import { BaseLlmClient } from '../core/baseLlmClient.js';
|
|
8
8
|
import { debugLogger } from '../utils/debugLogger.js';
|
|
9
|
+
import { SESSION_FILE_PREFIX, } from './chatRecordingService.js';
|
|
10
|
+
import fs from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
const MIN_MESSAGES_FOR_SUMMARY = 1;
|
|
9
13
|
/**
|
|
10
|
-
* Generates and saves a summary for
|
|
11
|
-
* This is called during session cleanup and is non-blocking - errors are logged but don't prevent exit.
|
|
14
|
+
* Generates and saves a summary for a session file.
|
|
12
15
|
*/
|
|
13
|
-
|
|
16
|
+
async function generateAndSaveSummary(config, sessionPath) {
|
|
17
|
+
// Read session file
|
|
18
|
+
const content = await fs.readFile(sessionPath, 'utf-8');
|
|
19
|
+
const conversation = JSON.parse(content);
|
|
20
|
+
// Skip if summary already exists
|
|
21
|
+
if (conversation.summary) {
|
|
22
|
+
debugLogger.debug(`[SessionSummary] Summary already exists for ${sessionPath}, skipping`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Skip if no messages
|
|
26
|
+
if (conversation.messages.length === 0) {
|
|
27
|
+
debugLogger.debug(`[SessionSummary] No messages to summarize in ${sessionPath}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Create summary service
|
|
31
|
+
const contentGenerator = config.getContentGenerator();
|
|
32
|
+
if (!contentGenerator) {
|
|
33
|
+
debugLogger.debug('[SessionSummary] Content generator not available, skipping summary generation');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const baseLlmClient = new BaseLlmClient(contentGenerator, config);
|
|
37
|
+
const summaryService = new SessionSummaryService(baseLlmClient);
|
|
38
|
+
// Generate summary
|
|
39
|
+
const summary = await summaryService.generateSummary({
|
|
40
|
+
messages: conversation.messages,
|
|
41
|
+
});
|
|
42
|
+
if (!summary) {
|
|
43
|
+
debugLogger.warn(`[SessionSummary] Failed to generate summary for ${sessionPath}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Re-read the file before writing to handle race conditions
|
|
47
|
+
const freshContent = await fs.readFile(sessionPath, 'utf-8');
|
|
48
|
+
const freshConversation = JSON.parse(freshContent);
|
|
49
|
+
// Check if summary was added by another process
|
|
50
|
+
if (freshConversation.summary) {
|
|
51
|
+
debugLogger.debug(`[SessionSummary] Summary was added by another process for ${sessionPath}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Add summary and write back
|
|
55
|
+
freshConversation.summary = summary;
|
|
56
|
+
freshConversation.lastUpdated = new Date().toISOString();
|
|
57
|
+
await fs.writeFile(sessionPath, JSON.stringify(freshConversation, null, 2));
|
|
58
|
+
debugLogger.debug(`[SessionSummary] Saved summary for ${sessionPath}: "${summary}"`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Finds the most recently created session that needs a summary.
|
|
62
|
+
* Returns the path if it needs a summary, null otherwise.
|
|
63
|
+
*/
|
|
64
|
+
export async function getPreviousSession(config) {
|
|
14
65
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!chatRecordingService) {
|
|
20
|
-
debugLogger.debug('[SessionSummary] No chat recording service available');
|
|
21
|
-
return;
|
|
66
|
+
const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats');
|
|
67
|
+
// Check if chats directory exists
|
|
68
|
+
try {
|
|
69
|
+
await fs.access(chatsDir);
|
|
22
70
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
debugLogger.debug('[SessionSummary] No conversation to summarize');
|
|
27
|
-
return;
|
|
71
|
+
catch {
|
|
72
|
+
debugLogger.debug('[SessionSummary] No chats directory found');
|
|
73
|
+
return null;
|
|
28
74
|
}
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
75
|
+
// List session files
|
|
76
|
+
const allFiles = await fs.readdir(chatsDir);
|
|
77
|
+
const sessionFiles = allFiles.filter((f) => f.startsWith(SESSION_FILE_PREFIX) && f.endsWith('.json'));
|
|
78
|
+
if (sessionFiles.length === 0) {
|
|
79
|
+
debugLogger.debug('[SessionSummary] No session files found');
|
|
80
|
+
return null;
|
|
33
81
|
}
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
82
|
+
// Sort by filename descending (most recently created first)
|
|
83
|
+
// Filename format: session-YYYY-MM-DDTHH-MM-XXXXXXXX.json
|
|
84
|
+
sessionFiles.sort((a, b) => b.localeCompare(a));
|
|
85
|
+
// Check the most recently created session
|
|
86
|
+
const mostRecentFile = sessionFiles[0];
|
|
87
|
+
const filePath = path.join(chatsDir, mostRecentFile);
|
|
88
|
+
try {
|
|
89
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
90
|
+
const conversation = JSON.parse(content);
|
|
91
|
+
if (conversation.summary) {
|
|
92
|
+
debugLogger.debug('[SessionSummary] Most recent session already has summary');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
// Only generate summaries for sessions with more than 1 user message
|
|
96
|
+
const userMessageCount = conversation.messages.filter((m) => m.type === 'user').length;
|
|
97
|
+
if (userMessageCount <= MIN_MESSAGES_FOR_SUMMARY) {
|
|
98
|
+
debugLogger.debug(`[SessionSummary] Most recent session has ${userMessageCount} user message(s), skipping (need more than ${MIN_MESSAGES_FOR_SUMMARY})`);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return filePath;
|
|
38
102
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const summaryService = new SessionSummaryService(baseLlmClient);
|
|
43
|
-
// Generate summary
|
|
44
|
-
const summary = await summaryService.generateSummary({
|
|
45
|
-
messages: conversation.messages,
|
|
46
|
-
});
|
|
47
|
-
// Save summary if generated successfully
|
|
48
|
-
if (summary) {
|
|
49
|
-
chatRecordingService.saveSummary(summary);
|
|
50
|
-
debugLogger.debug(`[SessionSummary] Saved summary: "${summary}"`);
|
|
103
|
+
catch {
|
|
104
|
+
debugLogger.debug('[SessionSummary] Could not read most recent session');
|
|
105
|
+
return null;
|
|
51
106
|
}
|
|
52
|
-
|
|
53
|
-
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
debugLogger.debug(`[SessionSummary] Error finding previous session: ${error instanceof Error ? error.message : String(error)}`);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Generates summary for the previous session if it lacks one.
|
|
115
|
+
* This is designed to be called fire-and-forget on startup.
|
|
116
|
+
*/
|
|
117
|
+
export async function generateSummary(config) {
|
|
118
|
+
try {
|
|
119
|
+
const sessionPath = await getPreviousSession(config);
|
|
120
|
+
if (sessionPath) {
|
|
121
|
+
await generateAndSaveSummary(config, sessionPath);
|
|
54
122
|
}
|
|
55
123
|
}
|
|
56
124
|
catch (error) {
|
|
57
125
|
// Log but don't throw - we want graceful degradation
|
|
58
|
-
debugLogger.warn(`[SessionSummary] Error
|
|
126
|
+
debugLogger.warn(`[SessionSummary] Error generating summary: ${error instanceof Error ? error.message : String(error)}`);
|
|
59
127
|
}
|
|
60
128
|
}
|
|
61
129
|
//# sourceMappingURL=sessionSummaryUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionSummaryUtils.js","sourceRoot":"","sources":["../../../src/services/sessionSummaryUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionSummaryUtils.js","sourceRoot":"","sources":["../../../src/services/sessionSummaryUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import {
|
|
7
|
+
import { generateSummary, getPreviousSession } from './sessionSummaryUtils.js';
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
// Mock fs/promises
|
|
11
|
+
vi.mock('node:fs/promises');
|
|
12
|
+
const mockReaddir = fs.readdir;
|
|
8
13
|
// Mock the SessionSummaryService module
|
|
9
14
|
vi.mock('./sessionSummaryService.js', () => ({
|
|
10
15
|
SessionSummaryService: vi.fn().mockImplementation(() => ({
|
|
@@ -15,31 +20,32 @@ vi.mock('./sessionSummaryService.js', () => ({
|
|
|
15
20
|
vi.mock('../core/baseLlmClient.js', () => ({
|
|
16
21
|
BaseLlmClient: vi.fn(),
|
|
17
22
|
}));
|
|
23
|
+
// Helper to create a session with N user messages
|
|
24
|
+
function createSessionWithUserMessages(count, options = {}) {
|
|
25
|
+
return JSON.stringify({
|
|
26
|
+
sessionId: options.sessionId ?? 'session-id',
|
|
27
|
+
summary: options.summary,
|
|
28
|
+
messages: Array.from({ length: count }, (_, i) => ({
|
|
29
|
+
id: String(i + 1),
|
|
30
|
+
type: 'user',
|
|
31
|
+
content: [{ text: `Message ${i + 1}` }],
|
|
32
|
+
})),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
18
35
|
describe('sessionSummaryUtils', () => {
|
|
19
36
|
let mockConfig;
|
|
20
|
-
let mockChatRecordingService;
|
|
21
|
-
let mockGeminiClient;
|
|
22
37
|
let mockContentGenerator;
|
|
23
38
|
let mockGenerateSummary;
|
|
24
39
|
beforeEach(async () => {
|
|
25
40
|
vi.clearAllMocks();
|
|
26
41
|
// Setup mock content generator
|
|
27
42
|
mockContentGenerator = {};
|
|
28
|
-
// Setup mock chat recording service
|
|
29
|
-
mockChatRecordingService = {
|
|
30
|
-
getConversation: vi.fn(),
|
|
31
|
-
saveSummary: vi.fn(),
|
|
32
|
-
};
|
|
33
|
-
// Setup mock gemini client
|
|
34
|
-
mockGeminiClient = {
|
|
35
|
-
getChatRecordingService: vi
|
|
36
|
-
.fn()
|
|
37
|
-
.mockReturnValue(mockChatRecordingService),
|
|
38
|
-
};
|
|
39
43
|
// Setup mock config
|
|
40
44
|
mockConfig = {
|
|
41
45
|
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
|
|
42
|
-
|
|
46
|
+
storage: {
|
|
47
|
+
getProjectTempDir: vi.fn().mockReturnValue('/tmp/project'),
|
|
48
|
+
},
|
|
43
49
|
};
|
|
44
50
|
// Setup mock generateSummary function
|
|
45
51
|
mockGenerateSummary = vi.fn().mockResolvedValue('Add dark mode to the app');
|
|
@@ -52,246 +58,79 @@ describe('sessionSummaryUtils', () => {
|
|
|
52
58
|
afterEach(() => {
|
|
53
59
|
vi.restoreAllMocks();
|
|
54
60
|
});
|
|
55
|
-
describe('
|
|
56
|
-
it('should
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
startTime: '2025-12-03T00:00:00Z',
|
|
61
|
-
lastUpdated: '2025-12-03T00:10:00Z',
|
|
62
|
-
messages: [
|
|
63
|
-
{
|
|
64
|
-
id: '1',
|
|
65
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
66
|
-
type: 'user',
|
|
67
|
-
content: [{ text: 'How do I add dark mode?' }],
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: '2',
|
|
71
|
-
timestamp: '2025-12-03T00:01:00Z',
|
|
72
|
-
type: 'gemini',
|
|
73
|
-
content: [{ text: 'To add dark mode...' }],
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
};
|
|
77
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
78
|
-
await generateAndSaveSummary(mockConfig);
|
|
79
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
80
|
-
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
81
|
-
expect(mockGenerateSummary).toHaveBeenCalledWith({
|
|
82
|
-
messages: mockConversation.messages,
|
|
83
|
-
});
|
|
84
|
-
expect(mockChatRecordingService.saveSummary).toHaveBeenCalledTimes(1);
|
|
85
|
-
expect(mockChatRecordingService.saveSummary).toHaveBeenCalledWith('Add dark mode to the app');
|
|
61
|
+
describe('getPreviousSession', () => {
|
|
62
|
+
it('should return null if chats directory does not exist', async () => {
|
|
63
|
+
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
|
|
64
|
+
const result = await getPreviousSession(mockConfig);
|
|
65
|
+
expect(result).toBeNull();
|
|
86
66
|
});
|
|
87
|
-
it('should
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
expect(
|
|
92
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
67
|
+
it('should return null if no session files exist', async () => {
|
|
68
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
69
|
+
mockReaddir.mockResolvedValue([]);
|
|
70
|
+
const result = await getPreviousSession(mockConfig);
|
|
71
|
+
expect(result).toBeNull();
|
|
93
72
|
});
|
|
94
|
-
it('should
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
expect(
|
|
73
|
+
it('should return null if most recent session already has summary', async () => {
|
|
74
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
75
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
76
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(5, { summary: 'Existing summary' }));
|
|
77
|
+
const result = await getPreviousSession(mockConfig);
|
|
78
|
+
expect(result).toBeNull();
|
|
100
79
|
});
|
|
101
|
-
it('should
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
summary: 'Existing summary',
|
|
108
|
-
messages: [
|
|
109
|
-
{
|
|
110
|
-
id: '1',
|
|
111
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
112
|
-
type: 'user',
|
|
113
|
-
content: [{ text: 'Hello' }],
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
};
|
|
117
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
118
|
-
await generateAndSaveSummary(mockConfig);
|
|
119
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
120
|
-
expect(mockGenerateSummary).not.toHaveBeenCalled();
|
|
121
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
80
|
+
it('should return null if most recent session has 1 or fewer user messages', async () => {
|
|
81
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
82
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
83
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(1));
|
|
84
|
+
const result = await getPreviousSession(mockConfig);
|
|
85
|
+
expect(result).toBeNull();
|
|
122
86
|
});
|
|
123
|
-
it('should
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
messages: [],
|
|
130
|
-
};
|
|
131
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
132
|
-
await generateAndSaveSummary(mockConfig);
|
|
133
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
134
|
-
expect(mockGenerateSummary).not.toHaveBeenCalled();
|
|
135
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
87
|
+
it('should return path if most recent session has more than 1 user message and no summary', async () => {
|
|
88
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
89
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
90
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(2));
|
|
91
|
+
const result = await getPreviousSession(mockConfig);
|
|
92
|
+
expect(result).toBe(path.join('/tmp/project', 'chats', 'session-2024-01-01T10-00-abc12345.json'));
|
|
136
93
|
});
|
|
137
|
-
it('should
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
147
|
-
type: 'user',
|
|
148
|
-
content: [{ text: 'Hello' }],
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
153
|
-
mockGenerateSummary.mockResolvedValue(null);
|
|
154
|
-
await generateAndSaveSummary(mockConfig);
|
|
155
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
156
|
-
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
157
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
94
|
+
it('should select most recently created session by filename', async () => {
|
|
95
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
96
|
+
mockReaddir.mockResolvedValue([
|
|
97
|
+
'session-2024-01-01T10-00-older000.json',
|
|
98
|
+
'session-2024-01-02T10-00-newer000.json',
|
|
99
|
+
]);
|
|
100
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(2));
|
|
101
|
+
const result = await getPreviousSession(mockConfig);
|
|
102
|
+
expect(result).toBe(path.join('/tmp/project', 'chats', 'session-2024-01-02T10-00-newer000.json'));
|
|
158
103
|
});
|
|
159
|
-
it('should
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
messages: [
|
|
166
|
-
{
|
|
167
|
-
id: '1',
|
|
168
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
169
|
-
type: 'user',
|
|
170
|
-
content: [{ text: 'Hello' }],
|
|
171
|
-
},
|
|
172
|
-
],
|
|
173
|
-
};
|
|
174
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
175
|
-
mockGenerateSummary.mockRejectedValue(new Error('API Error'));
|
|
176
|
-
// Should not throw
|
|
177
|
-
await expect(generateAndSaveSummary(mockConfig)).resolves.not.toThrow();
|
|
178
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
179
|
-
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
180
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
104
|
+
it('should return null if most recent session file is corrupted', async () => {
|
|
105
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
106
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
107
|
+
vi.mocked(fs.readFile).mockResolvedValue('invalid json');
|
|
108
|
+
const result = await getPreviousSession(mockConfig);
|
|
109
|
+
expect(result).toBeNull();
|
|
181
110
|
});
|
|
182
111
|
});
|
|
183
|
-
describe('
|
|
184
|
-
it('should
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
projectHash: 'test-hash',
|
|
188
|
-
startTime: '2025-12-03T00:00:00Z',
|
|
189
|
-
lastUpdated: '2025-12-03T00:10:00Z',
|
|
190
|
-
messages: [
|
|
191
|
-
{
|
|
192
|
-
id: '1',
|
|
193
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
194
|
-
type: 'user',
|
|
195
|
-
content: [{ text: 'Hello' }],
|
|
196
|
-
},
|
|
197
|
-
],
|
|
198
|
-
};
|
|
199
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
200
|
-
await generateAndSaveSummary(mockConfig);
|
|
201
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledTimes(1);
|
|
202
|
-
expect(mockChatRecordingService.getConversation).toHaveBeenCalledWith();
|
|
203
|
-
});
|
|
204
|
-
it('should call generateSummary() with correct messages', async () => {
|
|
205
|
-
const mockMessages = [
|
|
206
|
-
{
|
|
207
|
-
id: '1',
|
|
208
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
209
|
-
type: 'user',
|
|
210
|
-
content: [{ text: 'How do I add dark mode?' }],
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
id: '2',
|
|
214
|
-
timestamp: '2025-12-03T00:01:00Z',
|
|
215
|
-
type: 'gemini',
|
|
216
|
-
content: [{ text: 'To add dark mode...' }],
|
|
217
|
-
},
|
|
218
|
-
];
|
|
219
|
-
const mockConversation = {
|
|
220
|
-
sessionId: 'test-session',
|
|
221
|
-
projectHash: 'test-hash',
|
|
222
|
-
startTime: '2025-12-03T00:00:00Z',
|
|
223
|
-
lastUpdated: '2025-12-03T00:10:00Z',
|
|
224
|
-
messages: mockMessages,
|
|
225
|
-
};
|
|
226
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
227
|
-
await generateAndSaveSummary(mockConfig);
|
|
228
|
-
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
229
|
-
expect(mockGenerateSummary).toHaveBeenCalledWith({
|
|
230
|
-
messages: mockMessages,
|
|
231
|
-
});
|
|
112
|
+
describe('generateSummary', () => {
|
|
113
|
+
it('should not throw if getPreviousSession returns null', async () => {
|
|
114
|
+
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
|
|
115
|
+
await expect(generateSummary(mockConfig)).resolves.not.toThrow();
|
|
232
116
|
});
|
|
233
|
-
it('should
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
{
|
|
241
|
-
id: '1',
|
|
242
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
243
|
-
type: 'user',
|
|
244
|
-
content: [{ text: 'Hello' }],
|
|
245
|
-
},
|
|
246
|
-
],
|
|
247
|
-
};
|
|
248
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
249
|
-
mockGenerateSummary.mockResolvedValue('Test summary');
|
|
250
|
-
await generateAndSaveSummary(mockConfig);
|
|
251
|
-
expect(mockChatRecordingService.saveSummary).toHaveBeenCalledTimes(1);
|
|
252
|
-
expect(mockChatRecordingService.saveSummary).toHaveBeenCalledWith('Test summary');
|
|
253
|
-
});
|
|
254
|
-
it('should not call saveSummary() if generation fails', async () => {
|
|
255
|
-
const mockConversation = {
|
|
256
|
-
sessionId: 'test-session',
|
|
257
|
-
projectHash: 'test-hash',
|
|
258
|
-
startTime: '2025-12-03T00:00:00Z',
|
|
259
|
-
lastUpdated: '2025-12-03T00:10:00Z',
|
|
260
|
-
messages: [
|
|
261
|
-
{
|
|
262
|
-
id: '1',
|
|
263
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
264
|
-
type: 'user',
|
|
265
|
-
content: [{ text: 'Hello' }],
|
|
266
|
-
},
|
|
267
|
-
],
|
|
268
|
-
};
|
|
269
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
270
|
-
mockGenerateSummary.mockResolvedValue(null);
|
|
271
|
-
await generateAndSaveSummary(mockConfig);
|
|
117
|
+
it('should generate and save summary for session needing one', async () => {
|
|
118
|
+
const sessionPath = path.join('/tmp/project', 'chats', 'session-2024-01-01T10-00-abc12345.json');
|
|
119
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
120
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
121
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(2));
|
|
122
|
+
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
123
|
+
await generateSummary(mockConfig);
|
|
272
124
|
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
273
|
-
expect(
|
|
125
|
+
expect(fs.writeFile).toHaveBeenCalledTimes(1);
|
|
126
|
+
expect(fs.writeFile).toHaveBeenCalledWith(sessionPath, expect.stringContaining('Add dark mode to the app'));
|
|
274
127
|
});
|
|
275
|
-
it('should
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
messages: [
|
|
282
|
-
{
|
|
283
|
-
id: '1',
|
|
284
|
-
timestamp: '2025-12-03T00:00:00Z',
|
|
285
|
-
type: 'user',
|
|
286
|
-
content: [{ text: 'Hello' }],
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
};
|
|
290
|
-
mockChatRecordingService.getConversation.mockReturnValue(mockConversation);
|
|
291
|
-
mockGenerateSummary.mockRejectedValue(new Error('Generation failed'));
|
|
292
|
-
await generateAndSaveSummary(mockConfig);
|
|
293
|
-
expect(mockGenerateSummary).toHaveBeenCalledTimes(1);
|
|
294
|
-
expect(mockChatRecordingService.saveSummary).not.toHaveBeenCalled();
|
|
128
|
+
it('should handle errors gracefully without throwing', async () => {
|
|
129
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
130
|
+
mockReaddir.mockResolvedValue(['session-2024-01-01T10-00-abc12345.json']);
|
|
131
|
+
vi.mocked(fs.readFile).mockResolvedValue(createSessionWithUserMessages(2));
|
|
132
|
+
mockGenerateSummary.mockRejectedValue(new Error('API Error'));
|
|
133
|
+
await expect(generateSummary(mockConfig)).resolves.not.toThrow();
|
|
295
134
|
});
|
|
296
135
|
});
|
|
297
136
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionSummaryUtils.test.js","sourceRoot":"","sources":["../../../src/services/sessionSummaryUtils.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"sessionSummaryUtils.test.js","sourceRoot":"","sources":["../../../src/services/sessionSummaryUtils.test.ts"],"names":[],"mappings":"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"}
|