fix(chat): stabilize message identities

This commit is contained in:
2026-07-20 18:30:21 +08:00
parent 159e8bfd59
commit 1e13f94b5d
33 changed files with 718 additions and 212 deletions
+23 -3
View File
@@ -15,6 +15,8 @@ const log = new Logger("StoresChatChatHistorySync");
export type ReadAndSyncHistoryOutput = {
/** Network-authoritative messages. Empty network history includes a UI greeting. */
messages: UiMessage[];
/** Display identities present in the local snapshot before the request. */
localDisplayIds: readonly string[];
/** True when network history was written back to local storage. */
localOverwritten: boolean;
localCount: number;
@@ -31,8 +33,12 @@ export type LocalHistorySnapshotOutput = {
export type NetworkHistorySyncOutput = ReadAndSyncHistoryOutput;
export function createGreetingMessage(content: string): UiMessage {
export function createGreetingMessage(
characterId: string,
content: string,
): UiMessage {
return {
displayId: `greeting:${encodeURIComponent(characterId)}`,
content,
isFromAI: true,
date: todayString(),
@@ -49,10 +55,14 @@ export async function resolveHistoryCacheIdentity(
export async function readLocalHistorySnapshot(
cacheIdentity: string | null,
characterId: string,
emptyChatGreeting: string,
): Promise<LocalHistorySnapshotOutput> {
const chatRepo = await loadChatRepository();
const greetingMessage = createGreetingMessage(emptyChatGreeting);
const greetingMessage = createGreetingMessage(
characterId,
emptyChatGreeting,
);
const localResult = cacheIdentity
? await chatRepo.getLocalMessages(cacheIdentity)
@@ -80,12 +90,16 @@ export async function readLocalHistorySnapshot(
export async function syncNetworkHistory(
characterId: string,
localCount: number,
localDisplayIds: readonly string[],
cacheIdentity: string | null,
emptyChatGreeting: string,
signal?: AbortSignal,
): Promise<NetworkHistorySyncOutput | null> {
const chatRepo = await loadChatRepository();
const greetingMessage = createGreetingMessage(emptyChatGreeting);
const greetingMessage = createGreetingMessage(
characterId,
emptyChatGreeting,
);
const networkResult = await chatRepo.getHistory(
characterId,
@@ -134,6 +148,7 @@ export async function syncNetworkHistory(
return {
messages: finalMessages,
localDisplayIds,
localOverwritten,
localCount,
networkCount: networkUi.length,
@@ -156,11 +171,13 @@ export async function readAndSyncHistory(
const cacheIdentity = await resolveHistoryCacheIdentity(characterId);
const localSnapshot = await readLocalHistorySnapshot(
cacheIdentity,
characterId,
emptyChatGreeting,
);
const networkSnapshot = await syncNetworkHistory(
characterId,
localSnapshot.localCount,
localSnapshot.messages.map((message) => message.displayId),
cacheIdentity,
emptyChatGreeting,
signal,
@@ -169,6 +186,9 @@ export async function readAndSyncHistory(
return {
messages: localSnapshot.messages,
localDisplayIds: localSnapshot.messages.map(
(message) => message.displayId,
),
localOverwritten: false,
localCount: localSnapshot.localCount,
networkCount: 0,