From 7161e934e08c759d91f387b64a2087ea1707b52b Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Tue, 15 Sep 2026 20:39:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=86=E5=8F=B2=E5=9B=9E=E8=AF=BB=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=B0=9A=E6=9C=AA=E8=90=BD=E7=9B=98=E7=9A=84=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E6=B6=88=E6=81=AF=EF=BC=9B=E8=BF=87=E7=A8=8B?= =?UTF-8?q?=E5=8D=A1=E4=B8=8D=E5=86=8D=E6=B8=B2=E6=9F=93=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E6=AD=A3=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ai-game-creator-shell/src/App.tsx | 50 +++++++++++++++++-- .../ProjectSupervisorView.tsx | 10 +--- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index a65717054..ff02c78bd 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -534,6 +534,41 @@ function claimInitialSupervisorMessageForPage(projectPath: string) { return true; } +/** + * 历史回读与"尚未落盘的运行时消息"合并。 + * + * 初始需求是**乐观插入**到 messages 的(latch 命中后先插一条 user 消息,再发起回合), + * 而历史回读在 replace 分支里是无条件整体替换 —— 只要回读晚于乐观插入,那条用户消息 + * 就会被冲掉(界面上看不到初始需求,但回合其实已经跑起来了)。 + * 这里把当前 messages 里"运行时拥有、且回读结果里没有"的消息保留在末尾(它们是最新的)。 + */ +function mergeLoadedConversationWithPendingRuntimeMessages( + loaded: ChatMessage[], + current: ChatMessage[], +): ChatMessage[] { + if (current.length === 0) { + return loaded; + } + const loadedIds = new Set( + loaded + .map((message) => message.messageId) + .filter((id): id is string => Boolean(id)), + ); + const loadedTexts = new Set( + loaded.map((message) => `${message.role}\u0000${message.text}`), + ); + const pending = current.filter((message) => { + if (!message.runtimeOwned) { + return false; + } + if (message.messageId) { + return !loadedIds.has(message.messageId); + } + return !loadedTexts.has(`${message.role}\u0000${message.text}`); + }); + return pending.length > 0 ? [...loaded, ...pending] : loaded; +} + export { AuthenticatedClient } from './app/AuthenticatedClient'; export type { PendingCommand } from './app/types'; export { @@ -3475,8 +3510,12 @@ export function App({ setProjectSupervisorResponseStream(null); } setConversationVisibleCount(CONVERSATION_INITIAL_VISIBLE_COUNT); - setMessages(() => { - const nextMessages = conversationMessages; + setMessages((current) => { + // 不能整体替换:乐观插入、尚未落盘的用户消息会被冲掉(初始需求看不到就是这个原因)。 + const nextMessages = mergeLoadedConversationWithPendingRuntimeMessages( + conversationMessages, + current, + ); savedConversationProjectPathRef.current = nextProjectPath; savedConversationCountRef.current = nextMessages.length; latestMessagesRef.current = nextMessages; @@ -3690,7 +3729,12 @@ export function App({ } setProjectSupervisorRuntimeError(runtimeError || resumeError); setMessages((current) => { - const nextConversationMessages = conversationMessages; + // replace 分支同样不能丢掉尚未落盘的运行时消息(初始需求)。 + const nextConversationMessages = + mergeLoadedConversationWithPendingRuntimeMessages( + conversationMessages, + current, + ); const hasOnlyDefaultGreeting = current.length === 1 && current[0]?.role === 'assistant' && diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index a0f965ca2..3358312d0 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -591,15 +591,7 @@ export function ProjectSupervisorView({ : '陶泥儿正在处理'} - {transientReply ? ( -
- -
- ) : null} + {directProcessDetail ? (