From 202113a41317d96468180ed47a97c8b7500e3f12 Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 30 Dec 2025 10:23:31 -0500 Subject: [PATCH 1/7] complete --- .../components/messages/ToolMessage.test.tsx | 1 + .../useToolScheduler.test.ts.snap | 1 + packages/core/src/core/client.test.ts | 73 ++++++------------- packages/core/src/core/client.ts | 14 +++- packages/core/src/core/coreToolScheduler.ts | 1 + packages/core/src/core/geminiChat.ts | 4 +- .../core/src/services/chatRecordingService.ts | 34 ++++++++- packages/core/src/telemetry/loggers.test.ts | 1 + packages/core/src/tools/edit.ts | 2 + packages/core/src/tools/tools.ts | 2 + packages/core/src/tools/write-file.ts | 2 + 11 files changed, 79 insertions(+), 56 deletions(-) diff --git a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx index 983bca8669e..fb01c4d9bce 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx @@ -182,6 +182,7 @@ describe('', () => { fileName: 'file.txt', originalContent: 'old', newContent: 'new', + filePath: 'file.txt', }; const { lastFrame } = renderWithContext( , diff --git a/packages/cli/src/ui/hooks/__snapshots__/useToolScheduler.test.ts.snap b/packages/cli/src/ui/hooks/__snapshots__/useToolScheduler.test.ts.snap index 871be0e7643..24ff4e13561 100644 --- a/packages/cli/src/ui/hooks/__snapshots__/useToolScheduler.test.ts.snap +++ b/packages/cli/src/ui/hooks/__snapshots__/useToolScheduler.test.ts.snap @@ -64,6 +64,7 @@ exports[`useReactToolScheduler > should handle tool requiring confirmation - can "resultDisplay": { "fileDiff": "Mock tool requires confirmation", "fileName": "mockToolRequiresConfirmation.ts", + "filePath": undefined, "newContent": undefined, "originalContent": undefined, }, diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index 6045088c048..abca958f1d3 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -397,6 +397,10 @@ describe('Gemini Client (client.ts)', () => { getHistory: vi.fn((_curated?: boolean) => chatHistory), setHistory: vi.fn(), getLastPromptTokenCount: vi.fn().mockReturnValue(originalTokenCount), + getChatRecordingService: vi.fn().mockReturnValue({ + getConversation: vi.fn().mockReturnValue(null), + getConversationFilePath: vi.fn().mockReturnValue(null), + }), }; client['chat'] = mockOriginalChat as GeminiChat; @@ -483,64 +487,35 @@ describe('Gemini Client (client.ts)', () => { newTokenCount: estimatedNewTokenCount, originalTokenCount: 100, }); - // IMPORTANT: The change in client.ts means setLastPromptTokenCount is NOT called on failure - expect( - uiTelemetryService.setLastPromptTokenCount, - ).not.toHaveBeenCalled(); }); - it('does not manipulate the source chat', async () => { + it('resumes the session file when compression succeeds', async () => { const { client, mockOriginalChat } = setup({ - originalTokenCount: 100, - newTokenCount: 200, - compressionStatus: - CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, + compressionStatus: CompressionStatus.COMPRESSED, }); - await client.tryCompressChat('prompt-id-4', false); + const mockRecordingService = { + getConversation: vi + .fn() + .mockReturnValue({ sessionId: 'session-123' }), + getConversationFilePath: vi + .fn() + .mockReturnValue('/path/to/session.json'), + }; - // On failure, the chat should NOT be replaced - expect(client['chat']).toBe(mockOriginalChat); - }); + // Ensure the original chat has the recording service available + mockOriginalChat.getChatRecordingService = vi + .fn() + .mockReturnValue(mockRecordingService); - it.skip('will not attempt to compress context after a failure', async () => { - const { client } = setup({ - originalTokenCount: 100, - newTokenCount: 200, - compressionStatus: - CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, - }); + await client.tryCompressChat('prompt-id-resume', false); - await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true - - // Mock the next call to return NOOP - vi.mocked( - ChatCompressionService.prototype.compress, - ).mockResolvedValueOnce({ - newHistory: null, - info: { - originalTokenCount: 0, - newTokenCount: 0, - compressionStatus: CompressionStatus.NOOP, + expect(client['startChat']).toHaveBeenCalledWith( + expect.anything(), // new history + { + conversation: { sessionId: 'session-123' }, + filePath: '/path/to/session.json', }, - }); - - // This call should now be a NOOP - const result = await client.tryCompressChat('prompt-id-5', false); - - expect(result.compressionStatus).toBe(CompressionStatus.NOOP); - expect(ChatCompressionService.prototype.compress).toHaveBeenCalledTimes( - 2, - ); - expect( - ChatCompressionService.prototype.compress, - ).toHaveBeenLastCalledWith( - expect.anything(), - 'prompt-id-5', - false, - expect.anything(), - expect.anything(), - true, // hasFailedCompressionAttempt ); }); }); diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 48da7e43e7e..bf70aa22007 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -972,7 +972,19 @@ export class GeminiClient { this.hasFailedCompressionAttempt || !force; } else if (info.compressionStatus === CompressionStatus.COMPRESSED) { if (newHistory) { - this.chat = await this.startChat(newHistory); + // capture current session data before resetting + const currentRecordingService = + this.getChat().getChatRecordingService(); + const conversation = currentRecordingService.getConversation(); + const filePath = currentRecordingService.getConversationFilePath(); + + let resumedData: ResumedSessionData | undefined; + + if (conversation && filePath) { + resumedData = { conversation, filePath }; + } + + this.chat = await this.startChat(newHistory, resumedData); this.updateTelemetryTokenCount(); this.forceFullIdeContext = true; } diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts index 20afc07b2cf..742725ac6bc 100644 --- a/packages/core/src/core/coreToolScheduler.ts +++ b/packages/core/src/core/coreToolScheduler.ts @@ -284,6 +284,7 @@ export class CoreToolScheduler { originalContent: waitingCall.confirmationDetails.originalContent, newContent: waitingCall.confirmationDetails.newContent, + filePath: waitingCall.confirmationDetails.filePath, }; } } diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts index 3dc91e1b6c6..e29c566a392 100644 --- a/packages/core/src/core/geminiChat.ts +++ b/packages/core/src/core/geminiChat.ts @@ -836,9 +836,7 @@ export class GeminiChat { toolCalls: CompletedToolCall[], ): void { const toolCallRecords = toolCalls.map((call) => { - const resultDisplayRaw = call.response?.resultDisplay; - const resultDisplay = - typeof resultDisplayRaw === 'string' ? resultDisplayRaw : undefined; + const resultDisplay = call.response?.resultDisplay; return { id: call.request.callId, diff --git a/packages/core/src/services/chatRecordingService.ts b/packages/core/src/services/chatRecordingService.ts index 0f4dab0f496..3d01459106a 100644 --- a/packages/core/src/services/chatRecordingService.ts +++ b/packages/core/src/services/chatRecordingService.ts @@ -16,6 +16,7 @@ import type { GenerateContentResponseUsageMetadata, } from '@google/genai'; import { debugLogger } from '../utils/debugLogger.js'; +import type { ToolResultDisplay } from '../tools/tools.js'; export const SESSION_FILE_PREFIX = 'session-'; @@ -53,7 +54,7 @@ export interface ToolCallRecord { // UI-specific fields for display purposes displayName?: string; description?: string; - resultDisplay?: string; + resultDisplay?: ToolResultDisplay; renderOutputAsMarkdown?: boolean; } @@ -407,11 +408,14 @@ export class ChatRecordingService { /** * Saves the conversation record; overwrites the file. */ - private writeConversation(conversation: ConversationRecord): void { + private writeConversation( + conversation: ConversationRecord, + allowEmpty: boolean = false, + ): void { try { if (!this.conversationFile) return; // Don't write the file yet until there's at least one message. - if (conversation.messages.length === 0) return; + if (conversation.messages.length === 0 && !allowEmpty) return; // Only write the file if this change would change the file. if (this.cachedLastConvData !== JSON.stringify(conversation, null, 2)) { @@ -492,4 +496,28 @@ export class ChatRecordingService { throw error; } } + + /** + * Rewinds the conversation to the state just before the specified message ID. + * All messages from (and including) the specified ID onwards are removed. + */ + rewindTo(messageId: string): ConversationRecord { + try { + const conversation = this.readConversation(); + const messageIndex = conversation.messages.findIndex( + (m) => m.id === messageId, + ); + + if (messageIndex === -1) { + return conversation; + } + + conversation.messages = conversation.messages.slice(0, messageIndex); + this.writeConversation(conversation, true); + return conversation; + } catch (error) { + debugLogger.error('Error rewinding conversation.', error); + throw error; + } + } } diff --git a/packages/core/src/telemetry/loggers.test.ts b/packages/core/src/telemetry/loggers.test.ts index 3dabc4a89d5..c0023a16802 100644 --- a/packages/core/src/telemetry/loggers.test.ts +++ b/packages/core/src/telemetry/loggers.test.ts @@ -1053,6 +1053,7 @@ describe('loggers', () => { resultDisplay: { fileDiff: 'diff', fileName: 'file.txt', + filePath: 'file.txt', originalContent: 'old content', newContent: 'new content', diffStat: { diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts index 3f71bdaad0e..7d49f75ab20 100644 --- a/packages/core/src/tools/edit.ts +++ b/packages/core/src/tools/edit.ts @@ -818,9 +818,11 @@ class EditToolInvocation displayResult = { fileDiff, fileName, + filePath: this.params.file_path, originalContent: editData.currentContent, newContent: editData.newContent, diffStat, + isNewFile: editData.isNewFile }; } diff --git a/packages/core/src/tools/tools.ts b/packages/core/src/tools/tools.ts index 1b6f6f92ee2..d3efd56ec1f 100644 --- a/packages/core/src/tools/tools.ts +++ b/packages/core/src/tools/tools.ts @@ -647,9 +647,11 @@ export interface Todo { export interface FileDiff { fileDiff: string; fileName: string; + filePath: string; originalContent: string | null; newContent: string; diffStat?: DiffStat; + isNewFile?: boolean; } export interface DiffStat { diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts index 339a60b4b69..3dbb696acca 100644 --- a/packages/core/src/tools/write-file.ts +++ b/packages/core/src/tools/write-file.ts @@ -346,9 +346,11 @@ class WriteFileToolInvocation extends BaseToolInvocation< const displayResult: FileDiff = { fileDiff, fileName, + filePath: this.resolvedPath, originalContent: correctedContentResult.originalContent, newContent: correctedContentResult.correctedContent, diffStat, + isNewFile, }; return { From 8aa940f1bb63a79f889510fcbdd54f0aecc587f8 Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 30 Dec 2025 10:41:19 -0500 Subject: [PATCH 2/7] fix tests --- packages/core/src/core/client.test.ts | 98 +++++++++++++++++++++------ 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index abca958f1d3..16f78d40d82 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -38,6 +38,7 @@ import { ideContextStore } from '../ide/ideContext.js'; import type { ModelRouterService } from '../routing/modelRouterService.js'; import { uiTelemetryService } from '../telemetry/uiTelemetry.js'; import { ChatCompressionService } from '../services/chatCompressionService.js'; +import type { ChatRecordingService } from '../services/chatRecordingService.js'; import { createAvailabilityServiceMock } from '../availability/testUtils.js'; import type { ModelAvailabilityService } from '../availability/modelAvailabilityService.js'; import type { @@ -487,35 +488,64 @@ describe('Gemini Client (client.ts)', () => { newTokenCount: estimatedNewTokenCount, originalTokenCount: 100, }); + // IMPORTANT: The change in client.ts means setLastPromptTokenCount is NOT called on failure + expect( + uiTelemetryService.setLastPromptTokenCount, + ).not.toHaveBeenCalled(); }); - it('resumes the session file when compression succeeds', async () => { + it('does not manipulate the source chat', async () => { const { client, mockOriginalChat } = setup({ - compressionStatus: CompressionStatus.COMPRESSED, + originalTokenCount: 100, + newTokenCount: 200, + compressionStatus: + CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, }); - const mockRecordingService = { - getConversation: vi - .fn() - .mockReturnValue({ sessionId: 'session-123' }), - getConversationFilePath: vi - .fn() - .mockReturnValue('/path/to/session.json'), - }; + await client.tryCompressChat('prompt-id-4', false); - // Ensure the original chat has the recording service available - mockOriginalChat.getChatRecordingService = vi - .fn() - .mockReturnValue(mockRecordingService); + // On failure, the chat should NOT be replaced + expect(client['chat']).toBe(mockOriginalChat); + }); - await client.tryCompressChat('prompt-id-resume', false); + it.skip('will not attempt to compress context after a failure', async () => { + const { client } = setup({ + originalTokenCount: 100, + newTokenCount: 200, + compressionStatus: + CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT, + }); - expect(client['startChat']).toHaveBeenCalledWith( - expect.anything(), // new history - { - conversation: { sessionId: 'session-123' }, - filePath: '/path/to/session.json', + await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true + + // Mock the next call to return NOOP + vi.mocked( + ChatCompressionService.prototype.compress, + ).mockResolvedValueOnce({ + newHistory: null, + info: { + originalTokenCount: 0, + newTokenCount: 0, + compressionStatus: CompressionStatus.NOOP, }, + }); + + // This call should now be a NOOP + const result = await client.tryCompressChat('prompt-id-5', false); + + expect(result.compressionStatus).toBe(CompressionStatus.NOOP); + expect(ChatCompressionService.prototype.compress).toHaveBeenCalledTimes( + 2, + ); + expect( + ChatCompressionService.prototype.compress, + ).toHaveBeenLastCalledWith( + expect.anything(), + 'prompt-id-5', + false, + expect.anything(), + expect.anything(), + true, // hasFailedCompressionAttempt ); }); }); @@ -592,6 +622,34 @@ describe('Gemini Client (client.ts)', () => { newTokenCount: 50, }); }); + + it('should resume the session file when compression succeeds', async () => { + const { client, mockOriginalChat } = setup({ + compressionStatus: CompressionStatus.COMPRESSED, + }); + + const mockConversation = { some: 'conversation' }; + const mockFilePath = '/tmp/session.json'; + + // Override the mock to return values + const mockRecordingService = { + getConversation: vi.fn().mockReturnValue(mockConversation), + getConversationFilePath: vi.fn().mockReturnValue(mockFilePath), + }; + vi.mocked(mockOriginalChat.getChatRecordingService!).mockReturnValue( + mockRecordingService as unknown as ChatRecordingService, + ); + + await client.tryCompressChat('prompt-id', false); + + expect(client['startChat']).toHaveBeenCalledWith( + expect.anything(), // newHistory + { + conversation: mockConversation, + filePath: mockFilePath, + }, + ); + }); }); describe('sendMessageStream', () => { From 5309438479d502a408c63773e134237fc6cacff6 Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 6 Jan 2026 17:03:05 -0500 Subject: [PATCH 3/7] address 3 nits and write 2 tests for rewindTo function --- .../src/services/chatRecordingService.test.ts | 51 +++++++++++++++++++ .../core/src/services/chatRecordingService.ts | 28 +++++----- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/packages/core/src/services/chatRecordingService.test.ts b/packages/core/src/services/chatRecordingService.test.ts index dcd77c986fd..b67ef47d757 100644 --- a/packages/core/src/services/chatRecordingService.test.ts +++ b/packages/core/src/services/chatRecordingService.test.ts @@ -401,4 +401,55 @@ describe('ChatRecordingService', () => { ); }); }); + + describe('rewindTo', () => { + it('should rewind the conversation to a specific message ID', () => { + chatRecordingService.initialize(); + const initialConversation = { + sessionId: 'test-session-id', + projectHash: 'test-project-hash', + messages: [ + { id: '1', type: 'user', content: 'msg1' }, + { id: '2', type: 'gemini', content: 'msg2' }, + { id: '3', type: 'user', content: 'msg3' }, + ], + }; + vi.spyOn(fs, 'readFileSync').mockReturnValue( + JSON.stringify(initialConversation), + ); + const writeFileSyncSpy = vi + .spyOn(fs, 'writeFileSync') + .mockImplementation(() => undefined); + + const result = chatRecordingService.rewindTo('2'); + + expect(result.messages).toHaveLength(1); + expect(result.messages[0].id).toBe('1'); + expect(writeFileSyncSpy).toHaveBeenCalled(); + const savedConversation = JSON.parse( + writeFileSyncSpy.mock.calls[0][1] as string, + ) as ConversationRecord; + expect(savedConversation.messages).toHaveLength(1); + }); + + it('should return the original conversation if the message ID is not found', () => { + chatRecordingService.initialize(); + const initialConversation = { + sessionId: 'test-session-id', + projectHash: 'test-project-hash', + messages: [{ id: '1', type: 'user', content: 'msg1' }], + }; + vi.spyOn(fs, 'readFileSync').mockReturnValue( + JSON.stringify(initialConversation), + ); + const writeFileSyncSpy = vi + .spyOn(fs, 'writeFileSync') + .mockImplementation(() => undefined); + + const result = chatRecordingService.rewindTo('non-existent'); + + expect(result.messages).toHaveLength(1); + expect(writeFileSyncSpy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/core/src/services/chatRecordingService.ts b/packages/core/src/services/chatRecordingService.ts index 3d01459106a..0bd650d2508 100644 --- a/packages/core/src/services/chatRecordingService.ts +++ b/packages/core/src/services/chatRecordingService.ts @@ -410,7 +410,7 @@ export class ChatRecordingService { */ private writeConversation( conversation: ConversationRecord, - allowEmpty: boolean = false, + { allowEmpty = false }: { allowEmpty?: boolean } = {}, ): void { try { if (!this.conversationFile) return; @@ -502,22 +502,20 @@ export class ChatRecordingService { * All messages from (and including) the specified ID onwards are removed. */ rewindTo(messageId: string): ConversationRecord { - try { - const conversation = this.readConversation(); - const messageIndex = conversation.messages.findIndex( - (m) => m.id === messageId, - ); - - if (messageIndex === -1) { - return conversation; - } + const conversation = this.readConversation(); + const messageIndex = conversation.messages.findIndex( + (m) => m.id === messageId, + ); - conversation.messages = conversation.messages.slice(0, messageIndex); - this.writeConversation(conversation, true); + if (messageIndex === -1) { + debugLogger.error( + 'Message to rewind to not found in conversation history', + ); return conversation; - } catch (error) { - debugLogger.error('Error rewinding conversation.', error); - throw error; } + + conversation.messages = conversation.messages.slice(0, messageIndex); + this.writeConversation(conversation, { allowEmpty: true }); + return conversation; } } From 9dfe086685a23a18051fa8de23d6c89a45fc6d9e Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 6 Jan 2026 17:28:13 -0500 Subject: [PATCH 4/7] restore assignment and add another check --- packages/core/src/core/geminiChat.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts index e29c566a392..3bc928c6fb6 100644 --- a/packages/core/src/core/geminiChat.ts +++ b/packages/core/src/core/geminiChat.ts @@ -836,7 +836,12 @@ export class GeminiChat { toolCalls: CompletedToolCall[], ): void { const toolCallRecords = toolCalls.map((call) => { - const resultDisplay = call.response?.resultDisplay; + const resultDisplayRaw = call.response?.resultDisplay; + const resultDisplay = + typeof resultDisplayRaw === 'string' || + (typeof resultDisplayRaw === 'object' && resultDisplayRaw !== null) + ? resultDisplayRaw + : undefined; return { id: call.request.callId, From e7dd1a1c9831e468a6e26436793bc75f3332d7bb Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 6 Jan 2026 17:57:55 -0500 Subject: [PATCH 5/7] format file --- packages/core/src/tools/edit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts index 7d49f75ab20..85c86ff804b 100644 --- a/packages/core/src/tools/edit.ts +++ b/packages/core/src/tools/edit.ts @@ -818,11 +818,11 @@ class EditToolInvocation displayResult = { fileDiff, fileName, - filePath: this.params.file_path, + filePath: this.params.file_path, originalContent: editData.currentContent, newContent: editData.newContent, diffStat, - isNewFile: editData.isNewFile + isNewFile: editData.isNewFile, }; } From 6626804513c08d1edbb038612da9266a290272ee Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 6 Jan 2026 18:08:15 -0500 Subject: [PATCH 6/7] check conversation file exists before reading --- packages/core/src/services/chatRecordingService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/services/chatRecordingService.ts b/packages/core/src/services/chatRecordingService.ts index 0bd650d2508..b308cce789e 100644 --- a/packages/core/src/services/chatRecordingService.ts +++ b/packages/core/src/services/chatRecordingService.ts @@ -501,7 +501,10 @@ export class ChatRecordingService { * Rewinds the conversation to the state just before the specified message ID. * All messages from (and including) the specified ID onwards are removed. */ - rewindTo(messageId: string): ConversationRecord { + rewindTo(messageId: string): ConversationRecord | null { + if (!this.conversationFile) { + return null; + } const conversation = this.readConversation(); const messageIndex = conversation.messages.findIndex( (m) => m.id === messageId, From 588f46c5617a3b8f958f9b7708acac8f939ec0fa Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Tue, 6 Jan 2026 18:15:37 -0500 Subject: [PATCH 7/7] fix tests --- packages/core/src/services/chatRecordingService.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/services/chatRecordingService.test.ts b/packages/core/src/services/chatRecordingService.test.ts index b67ef47d757..6fb49fbd5fd 100644 --- a/packages/core/src/services/chatRecordingService.test.ts +++ b/packages/core/src/services/chatRecordingService.test.ts @@ -423,6 +423,7 @@ describe('ChatRecordingService', () => { const result = chatRecordingService.rewindTo('2'); + if (!result) throw new Error('Result should not be null'); expect(result.messages).toHaveLength(1); expect(result.messages[0].id).toBe('1'); expect(writeFileSyncSpy).toHaveBeenCalled(); @@ -448,6 +449,7 @@ describe('ChatRecordingService', () => { const result = chatRecordingService.rewindTo('non-existent'); + if (!result) throw new Error('Result should not be null'); expect(result.messages).toHaveLength(1); expect(writeFileSyncSpy).not.toHaveBeenCalled(); });