From e5655b4bd7d2c339d93d44c07dc9ded7dcf2250c Mon Sep 17 00:00:00 2001 From: yuanyuanAli <135116774+yuanyuanAli@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:42:58 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"fix(core):=20skip=20auto-title=20gene?= =?UTF-8?q?ration=20when=20history=20has=20no=20user=20messag=E2=80=A6"=20?= =?UTF-8?q?(#5200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 99d4c1246eeb8dc1c8336b9893d6922584c3db50. --- .../chatRecordingService.autoTitle.test.ts | 57 ------------------- .../core/src/services/sessionTitle.test.ts | 33 ----------- packages/core/src/services/sessionTitle.ts | 18 ------ 3 files changed, 108 deletions(-) diff --git a/packages/core/src/services/chatRecordingService.autoTitle.test.ts b/packages/core/src/services/chatRecordingService.autoTitle.test.ts index 3e962676b4..5061afe454 100644 --- a/packages/core/src/services/chatRecordingService.autoTitle.test.ts +++ b/packages/core/src/services/chatRecordingService.autoTitle.test.ts @@ -549,61 +549,4 @@ describe('ChatRecordingService - auto-title trigger', () => { expect(chatRecordingService.getCurrentCustomTitle()).toBe('user-chosen'); expect(chatRecordingService.getCurrentTitleSource()).toBe('manual'); }); - - it('skips title generation when history has no user message', async () => { - /** - * This test verifies that auto-title is not triggered when the history - * contains only model messages (e.g., initialization context) without - * any user message. This prevents titles like "初始化项目上下文" when - * the user hasn't sent any message yet. - */ - - // Simulate tryGenerateSessionTitle returning empty_history because - // the history only has model messages (initialization context) - tryGenerateSessionTitleMock.mockResolvedValueOnce({ - ok: false, - reason: 'empty_history', - }); - - // Simulate the first assistant turn completing with initialization context - chatRecordingService.recordAssistantTurn({ - model: 'qwen-plus', - message: [{ text: '已加载项目上下文,准备就绪。' }], - }); - - await flushMicrotasks(); - - // Verify: the mock was called and returned empty_history - expect(tryGenerateSessionTitleMock).toHaveBeenCalledOnce(); - // Verify: NO title was generated because history had no user message - const titleRecord = findCustomTitleRecord(); - expect(titleRecord).toBeUndefined(); - expect(chatRecordingService.getCurrentCustomTitle()).toBeUndefined(); - - // Now simulate user sends "你好" and gets a response - vi.mocked(jsonl.writeLine).mockClear(); - tryGenerateSessionTitleMock.mockClear(); - - // This time, tryGenerateSessionTitle succeeds because history now has user message - tryGenerateSessionTitleMock.mockResolvedValueOnce({ - ok: true, - title: '问候会话', - modelUsed: 'qwen-turbo', - }); - - chatRecordingService.recordAssistantTurn({ - model: 'qwen-plus', - message: [{ text: '你好!有什么可以帮助你的?' }], - }); - - await flushMicrotasks(); - - // Now the title should be generated based on the actual conversation - const newTitleRecord = findCustomTitleRecord(); - expect(newTitleRecord).toBeDefined(); - expect( - (newTitleRecord?.systemPayload as { customTitle?: string })?.customTitle, - ).toBe('问候会话'); - expect(chatRecordingService.getCurrentCustomTitle()).toBe('问候会话'); - }); }); diff --git a/packages/core/src/services/sessionTitle.test.ts b/packages/core/src/services/sessionTitle.test.ts index bc2eceadf2..2da674f754 100644 --- a/packages/core/src/services/sessionTitle.test.ts +++ b/packages/core/src/services/sessionTitle.test.ts @@ -75,39 +75,6 @@ describe('tryGenerateSessionTitle', () => { expect(outcome).toEqual({ ok: false, reason: 'empty_history' }); }); - it('returns {ok:false, reason:"empty_history"} when history has only startup prelude (no real user message)', async () => { - // Guard: auto-title should not be generated based solely on the - // initialization prelude — a role:'user' message - // seeded by getInitialChatHistory. Without the guard, the title model - // would be called with only the startup context and produce a title - // like "初始化项目上下文" instead of one based on the user's actual - // first message. - const { config, generateJson } = makeConfig({ - fastModel: 'qwen-turbo', - history: [ - { - role: 'user', - parts: [ - { - text: "\nThis is the Qwen Code. We are setting up the context for our chat.\nToday's date is Monday, June 15, 2026.\nMy operating system is: linux\nI'm currently working in the directory: /home/me/project\n", - }, - ], - }, - { - role: 'model', - parts: [{ text: 'Got it! How can I help you today?' }], - }, - ], - }); - const outcome = await tryGenerateSessionTitle( - config, - new AbortController().signal, - ); - expect(outcome).toEqual({ ok: false, reason: 'empty_history' }); - // The title model must NOT be called — the guard should short-circuit. - expect(generateJson).not.toHaveBeenCalled(); - }); - it('returns {ok:false, reason:"model_error"} when the LLM throws', async () => { const { config } = makeConfig({ fastModel: 'qwen-turbo', diff --git a/packages/core/src/services/sessionTitle.ts b/packages/core/src/services/sessionTitle.ts index 3a412c283c..b82c2028ad 100644 --- a/packages/core/src/services/sessionTitle.ts +++ b/packages/core/src/services/sessionTitle.ts @@ -7,7 +7,6 @@ import type { Content } from '@google/genai'; import type { Config } from '../config/config.js'; import { createDebugLogger } from '../utils/debugLogger.js'; -import { isSystemReminderContent } from '../utils/environmentContext.js'; import { runSideQuery } from '../utils/sideQuery.js'; import { stripTerminalControlSequences } from '../utils/terminalSafe.js'; import { SESSION_TITLE_MAX_LENGTH } from './sessionService.js'; @@ -117,23 +116,6 @@ export async function tryGenerateSessionTitle( const fullHistory = geminiClient.getHistoryShallow(); if (fullHistory.length < 2) return { ok: false, reason: 'empty_history' }; - // Guard: skip title generation if no real user message exists in history. - // This prevents the title from being generated based solely on - // initialization context (e.g., workspace loading, system prompts) - // before the user has actually said anything. Without this guard, a - // session that sends a prompt immediately after creation could get a - // title like "初始化项目上下文" instead of one based on the user's - // actual message. - // - // The startup prelude is a role:'user' message wrapping - // content (see getInitialChatHistory), so we must - // exclude it via isSystemReminderContent to avoid misclassifying it - // as a real user turn. - const hasUserMessage = fullHistory.some( - (msg) => msg.role === 'user' && !isSystemReminderContent(msg), - ); - if (!hasUserMessage) return { ok: false, reason: 'empty_history' }; - const dialog = filterToDialog(fullHistory); const recentHistory = takeRecentDialog(dialog, RECENT_MESSAGE_WINDOW); if (recentHistory.length === 0) {