diff options
Diffstat (limited to 'src/features/conversation-cache/cache.ts')
| -rw-r--r-- | src/features/conversation-cache/cache.ts | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/src/features/conversation-cache/cache.ts b/src/features/conversation-cache/cache.ts index 431a2a7..600a6e6 100644 --- a/src/features/conversation-cache/cache.ts +++ b/src/features/conversation-cache/cache.ts @@ -6,6 +6,19 @@ export interface ConversationCache { historyEpoch(conversationId: string): Promise<number | undefined>; reset(conversationId: string, historyEpoch: number): Promise<void>; + /** + * Replace from a fresh full-history response, including an epoch decrease. + * Only succeeds if the cache still has the epoch observed BEFORE that request. + * Unlike monotonic reset/commit, this is for confirmed server authority, not + * broadcasts or cursor/backfill responses. False means the response is stale. + */ + replace( + conversationId: string, + incoming: readonly StoredChunk[], + historyEpoch: number, + expectedEpoch: number | undefined, + ): Promise<boolean>; + /** Load all cached chunks for a conversation. */ load(conversationId: string): Promise<readonly StoredChunk[]>; @@ -73,6 +86,13 @@ export function createConversationCache( const current = await store.historyEpoch(id); if (current === undefined || current < epoch) await store.reset(id, epoch); }), + replace: (id, incoming, epoch, expectedEpoch) => + exclusive(id, async () => { + if ((await store.historyEpoch(id)) !== expectedEpoch) return false; + await store.reset(id, epoch); + if (incoming.length > 0) await store.append(id, incoming); + return true; + }), async load(conversationId) { await pending.get(conversationId); return store.load(conversationId); @@ -82,7 +102,9 @@ export function createConversationCache( return exclusive(conversationId, async () => { if (epoch !== undefined) { const current = await store.historyEpoch(conversationId); - if (current !== undefined && current > epoch) return store.load(conversationId); + // Once an epoch is known, only explicit invalidation or guarded + // full-history replacement may change it (in either direction). + if (current !== undefined && current !== epoch) return store.load(conversationId); if (current !== epoch) await store.reset(conversationId, epoch); } const cached = await store.load(conversationId); |
