summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/conversation-store/src/store.test.ts23
-rw-r--r--packages/conversation-store/src/store.ts17
2 files changed, 39 insertions, 1 deletions
diff --git a/packages/conversation-store/src/store.test.ts b/packages/conversation-store/src/store.test.ts
index 65c6aed..b119e2a 100644
--- a/packages/conversation-store/src/store.test.ts
+++ b/packages/conversation-store/src/store.test.ts
@@ -301,6 +301,29 @@ describe("ConversationStore", () => {
expect(chunks[0]?.chunk).toEqual({ type: "text", text: "c" });
});
+ it("loadSince treats a non-positive / non-integer sinceSeq as 0 (from the start), honoring the contract", async () => {
+ const store = createConversationStore(storage);
+ const messages: ChatMessage[] = [
+ { role: "user", chunks: [{ type: "text", text: "a" }] },
+ { role: "assistant", chunks: [{ type: "text", text: "b" }] },
+ { role: "user", chunks: [{ type: "text", text: "c" }] },
+ ];
+ await store.append("conv1", messages);
+ const all = [1, 2, 3];
+ // Non-positive integers → from the start (already worked; now codified).
+ expect((await store.loadSince("conv1", 0)).map((c) => c.seq)).toEqual(all);
+ expect((await store.loadSince("conv1", -2)).map((c) => c.seq)).toEqual(all);
+ // Non-integer values → from the start (the contract lie this fixes:
+ // a positive non-integer like 2.5 used to filter like sinceSeq=2).
+ expect((await store.loadSince("conv1", 2.5)).map((c) => c.seq)).toEqual(all);
+ expect((await store.loadSince("conv1", 2.7)).map((c) => c.seq)).toEqual(all);
+ expect((await store.loadSince("conv1", -2.5)).map((c) => c.seq)).toEqual(all);
+ expect((await store.loadSince("conv1", Number.POSITIVE_INFINITY)).map((c) => c.seq)).toEqual(
+ all,
+ );
+ expect((await store.loadSince("conv1", Number.NaN)).map((c) => c.seq)).toEqual(all);
+ });
+
it("load() round-trips the exact ChatMessage[] that was appended", async () => {
const store = createConversationStore(storage);
const messages: ChatMessage[] = [
diff --git a/packages/conversation-store/src/store.ts b/packages/conversation-store/src/store.ts
index fdbb2fb..ef5654b 100644
--- a/packages/conversation-store/src/store.ts
+++ b/packages/conversation-store/src/store.ts
@@ -77,6 +77,21 @@ function positiveInt(value: number | undefined): number | undefined {
return value;
}
+/**
+ * Coerce `sinceSeq` to a non-negative integer lower bound, honoring the
+ * contract's stated forgivingness for DIRECT callers: omitted / `0` /
+ * non-positive / non-integer (incl. `NaN`/`Infinity`) all → `0` (= "from the
+ * start"). A valid non-negative integer is returned as-is. The transport layer
+ * 400s these upstream, but `loadSince` stays total on its own. Keeps `loadSince`
+ * byte-identical to the prior `?? 0` behavior for the only values any caller
+ * ever passed (omitted / `0` / non-negative integers).
+ */
+function sinceSeqBase(value: number | undefined): number {
+ if (value === undefined) return 0;
+ if (!Number.isInteger(value) || value < 0) return 0;
+ return value;
+}
+
interface PersistedChunkEntry {
readonly chunk: Chunk;
readonly role: Role;
@@ -164,7 +179,7 @@ export function createConversationStore(
const sorted = [...keys].sort();
const result: StoredChunk[] = [];
- const minSeq = sinceSeq ?? 0;
+ const minSeq = sinceSeqBase(sinceSeq);
// Forgiving: a non-positive / non-integer bound is treated as ABSENT.
const beforeSeq = positiveInt(window?.beforeSeq);
const limit = positiveInt(window?.limit);