





















@@ -194,6 +194,36 @@ function messageDisplaySignature(message: unknown): string | null {
194194}
195195}
196196197+function messageTimestampMs(message: unknown): number | null {
198+if (!message || typeof message !== "object") {
199+return null;
200+}
201+const timestamp = (message as { timestamp?: unknown; ts?: unknown }).timestamp;
202+if (typeof timestamp === "number" && Number.isFinite(timestamp)) {
203+return timestamp;
204+}
205+const ts = (message as { timestamp?: unknown; ts?: unknown }).ts;
206+return typeof ts === "number" && Number.isFinite(ts) ? ts : null;
207+}
208+209+function historyHasSameOrNewerDisplayMessage(
210+historyMessages: unknown[],
211+signature: string,
212+message: unknown,
213+): boolean {
214+const timestamp = messageTimestampMs(message);
215+if (timestamp == null) {
216+return false;
217+}
218+return historyMessages.some((historyMessage) => {
219+if (messageDisplaySignature(historyMessage) !== signature) {
220+return false;
221+}
222+const historyTimestamp = messageTimestampMs(historyMessage);
223+return historyTimestamp != null && historyTimestamp >= timestamp;
224+});
225+}
226+197227export function preserveOptimisticTailMessages(
198228historyMessages: unknown[],
199229previousMessages: unknown[],
@@ -247,6 +277,34 @@ export function preserveOptimisticTailMessages(
247277return optimisticTail.length > 0 ? [...historyMessages, ...optimisticTail] : historyMessages;
248278}
249279280+function collectLateOptimisticTailMessages(
281+previousMessages: unknown[],
282+currentMessages: unknown[],
283+historyMessages: unknown[],
284+): unknown[] {
285+if (currentMessages === previousMessages || currentMessages.length <= previousMessages.length) {
286+return [];
287+}
288+if (previousMessages.some((message, index) => currentMessages[index] !== message)) {
289+return [];
290+}
291+const lateTail: unknown[] = [];
292+for (const message of currentMessages.slice(previousMessages.length)) {
293+if (!isLocallyOptimisticHistoryMessage(message) || shouldHideHistoryMessage(message)) {
294+return [];
295+}
296+const signature = messageDisplaySignature(message);
297+if (!signature) {
298+return [];
299+}
300+if (historyHasSameOrNewerDisplayMessage(historyMessages, signature, message)) {
301+continue;
302+}
303+lateTail.push(message);
304+}
305+return lateTail;
306+}
307+250308function isRetryableStartupUnavailable(err: unknown, method: string): err is GatewayRequestError {
251309if (!(err instanceof GatewayRequestError)) {
252310return false;
@@ -489,6 +547,7 @@ async function loadChatHistoryUncached(
489547const requestVersion = beginChatHistoryRequest(state);
490548const startedAt = Date.now();
491549const previousMessages = state.chatMessages;
550+const previousRunId = state.chatRunId;
492551// Any pending input-history snapshot becomes invalid once we start reloading transcript state.
493552state.resetChatInputHistoryNavigation?.();
494553state.chatLoading = true;
@@ -524,19 +583,29 @@ async function loadChatHistoryUncached(
524583}
525584const messages = Array.isArray(res.messages) ? res.messages : [];
526585const visibleMessages = messages.filter((message) => !shouldHideHistoryMessage(message));
586+const lateOptimisticTail = collectLateOptimisticTailMessages(
587+previousMessages,
588+state.chatMessages,
589+visibleMessages,
590+);
527591state.chatMessages = preserveOptimisticTailMessages(visibleMessages, previousMessages);
592+if (lateOptimisticTail.length > 0) {
593+state.chatMessages = [...state.chatMessages, ...lateOptimisticTail];
594+}
528595state.currentSessionId =
529596typeof res.sessionInfo?.sessionId === "string" && res.sessionInfo.sessionId.trim()
530597 ? res.sessionInfo.sessionId
531598 : typeof res.sessionId === "string" && res.sessionId.trim()
532599 ? res.sessionId
533600 : null;
534601state.chatThinkingLevel = res.sessionInfo?.thinkingLevel ?? res.thinkingLevel ?? null;
535-// Clear all streaming state — history includes tool results and text
536-// inline, so keeping streaming artifacts would cause duplicates.
537-maybeResetToolStream(state);
538-state.chatStream = null;
539-state.chatStreamStartedAt = null;
602+if (!state.chatRunId || state.chatRunId === previousRunId) {
603+// Clear all streaming state — history includes tool results and text
604+// inline, so keeping streaming artifacts would cause duplicates.
605+maybeResetToolStream(state);
606+state.chatStream = null;
607+state.chatStreamStartedAt = null;
608+}
540609return res;
541610} catch (err) {
542611if (!shouldApplyChatHistoryResult(state, requestVersion, sessionKey, requestAgentId)) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。