diff --git a/packages/cli/src/ui/commands/chatCommand.test.ts b/packages/cli/src/ui/commands/chatCommand.test.ts index 0fe485cadd..c934c29dfd 100644 --- a/packages/cli/src/ui/commands/chatCommand.test.ts +++ b/packages/cli/src/ui/commands/chatCommand.test.ts @@ -163,7 +163,6 @@ describe('chatCommand', () => { mockGetHistory.mockReturnValue([ { role: 'user', parts: [{ text: 'context for our chat' }] }, - { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] }, ]); result = await saveCommand?.action?.(mockContext, tag); expect(result).toEqual({ @@ -208,9 +207,7 @@ describe('chatCommand', () => { it('should save the conversation if overwrite is confirmed', async () => { const history: Content[] = [ { role: 'user', parts: [{ text: 'context for our chat' }] }, - { role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] }, { role: 'user', parts: [{ text: 'hello' }] }, - { role: 'model', parts: [{ text: 'Hi there!' }] }, ]; mockGetHistory.mockReturnValue(history); mockContext.overwriteConfirmed = true; @@ -263,6 +260,7 @@ describe('chatCommand', () => { it('should resume a conversation with matching authType', async () => { const conversation: Content[] = [ + { role: 'user', parts: [{ text: 'system setup' }] }, { role: 'user', parts: [{ text: 'hello gemini' }] }, { role: 'model', parts: [{ text: 'hello world' }] }, ]; @@ -285,6 +283,7 @@ describe('chatCommand', () => { it('should block resuming a conversation with mismatched authType', async () => { const conversation: Content[] = [ + { role: 'user', parts: [{ text: 'system setup' }] }, { role: 'user', parts: [{ text: 'hello gemini' }] }, { role: 'model', parts: [{ text: 'hello world' }] }, ]; @@ -304,6 +303,7 @@ describe('chatCommand', () => { it('should resume a legacy conversation without authType', async () => { const conversation: Content[] = [ + { role: 'user', parts: [{ text: 'system setup' }] }, { role: 'user', parts: [{ text: 'hello gemini' }] }, { role: 'model', parts: [{ text: 'hello world' }] }, ]; @@ -521,7 +521,6 @@ Hi there!`; it('should inform if there is no conversation to share', async () => { mockGetHistory.mockReturnValue([ { role: 'user', parts: [{ text: 'context' }] }, - { role: 'model', parts: [{ text: 'context response' }] }, ]); const result = await shareCommand?.action?.(mockContext, 'my-chat.json'); expect(mockFs.writeFile).not.toHaveBeenCalled(); diff --git a/packages/cli/src/ui/commands/chatCommand.ts b/packages/cli/src/ui/commands/chatCommand.ts index 82390be527..4b0078309c 100644 --- a/packages/cli/src/ui/commands/chatCommand.ts +++ b/packages/cli/src/ui/commands/chatCommand.ts @@ -17,6 +17,7 @@ import { CommandKind } from './types.js'; import { decodeTagName, type MessageActionReturn, + INITIAL_HISTORY_LENGTH, } from '@google/gemini-cli-core'; import path from 'node:path'; import type { @@ -131,7 +132,7 @@ const saveCommand: SlashCommand = { } const history = chat.getHistory(); - if (history.length > 2) { + if (history.length > INITIAL_HISTORY_LENGTH) { const authType = config?.getContentGeneratorConfig()?.authType; await logger.saveCheckpoint({ history, authType }, tag); return { @@ -200,11 +201,8 @@ const resumeCommand: SlashCommand = { }; const uiHistory: HistoryItemWithoutId[] = []; - let hasSystemPrompt = false; - let i = 0; - for (const item of conversation) { - i += 1; + for (const item of conversation.slice(INITIAL_HISTORY_LENGTH)) { const text = item.parts ?.filter((m) => !!m.text) @@ -213,15 +211,11 @@ const resumeCommand: SlashCommand = { if (!text) { continue; } - if (i === 1 && text.match(/context for our chat/)) { - hasSystemPrompt = true; - } - if (i > 2 || !hasSystemPrompt) { - uiHistory.push({ - type: (item.role && rolemap[item.role]) || MessageType.GEMINI, - text, - } as HistoryItemWithoutId); - } + + uiHistory.push({ + type: (item.role && rolemap[item.role]) || MessageType.GEMINI, + text, + } as HistoryItemWithoutId); } return { type: 'load_history', @@ -343,10 +337,10 @@ const shareCommand: SlashCommand = { const history = chat.getHistory(); - // An empty conversation has two hidden messages that setup the context for + // An empty conversation has a hidden message that sets up the context for // the chat. Thus, to check whether a conversation has been started, we // can't check for length 0. - if (history.length <= 2) { + if (history.length <= INITIAL_HISTORY_LENGTH) { return { type: 'message', messageType: 'info', diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c73b5d7b95..35804532d8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -73,6 +73,7 @@ export * from './utils/generateContentResponseUtilities.js'; export * from './utils/filesearch/fileSearch.js'; export * from './utils/errorParsing.js'; export * from './utils/workspaceContext.js'; +export * from './utils/environmentContext.js'; export * from './utils/ignorePatterns.js'; export * from './utils/partUtils.js'; export * from './utils/promptIdContext.js'; diff --git a/packages/core/src/utils/environmentContext.ts b/packages/core/src/utils/environmentContext.ts index 162c2e5961..cad628ce43 100644 --- a/packages/core/src/utils/environmentContext.ts +++ b/packages/core/src/utils/environmentContext.ts @@ -8,6 +8,8 @@ import type { Part, Content } from '@google/genai'; import type { Config } from '../config/config.js'; import { getFolderStructure } from './getFolderStructure.js'; +export const INITIAL_HISTORY_LENGTH = 1; + /** * Generates a string describing the current workspace directories and their structures. * @param {Config} config - The runtime configuration and services.