fix(eve): address review — async-write status, reasoning filter, turn-bounded capture, best-effort dedup
Review by @kartik-mem0. Root cause across all four: Mem0's hosted add with
infer:true is async and returns a PENDING event before the memory is visible.
- store.ts/tools.ts: add() returns AddResult{status,eventId} via parseAddResult;
the v3 add path returns {status:"PENDING", event_id}, so remember reports
{status:"queued", eventId} for async writes and {status:"saved"} only when
resolved. FAILED/unknown status maps to completed, not queued.
- messages.ts: drop typed non-text parts (reasoning no longer leaks into memory);
bound the captured assistant reply to the current turn and collect all of its
text segments (text->tool->text) instead of a stale prior answer.
- capture.ts + README + eve.mdx: document operationId dedup as best-effort
(lookup+add is not atomic; pending/concurrent windows can double-write);
removed the "restart replays cannot write twice" claim.
Tests: 68 pass (+15). Added reasoning, tool-only/cross-turn, multi-segment,
pending/concurrent capture replay, and PENDING/FAILED add-result cases.
Typecheck + build clean.
This commit is contained in:
@@ -25,11 +25,48 @@ vi.mock("mem0ai", () => ({
|
||||
import {
|
||||
createLazyStore,
|
||||
createMem0Store,
|
||||
parseAddResult,
|
||||
parseMemoryRecord,
|
||||
parseSearchHit,
|
||||
resolveApiKey,
|
||||
} from "../src/store.js";
|
||||
|
||||
describe("parseAddResult", () => {
|
||||
it("reports a pending event as queued", () => {
|
||||
expect(parseAddResult({ event_id: "evt_1", status: "PENDING" })).toEqual({
|
||||
status: "queued",
|
||||
eventId: "evt_1",
|
||||
});
|
||||
});
|
||||
|
||||
it("reports a succeeded event as completed", () => {
|
||||
expect(parseAddResult({ event_id: "evt_1", status: "SUCCEEDED" })).toEqual({
|
||||
status: "completed",
|
||||
eventId: "evt_1",
|
||||
});
|
||||
});
|
||||
|
||||
it("treats a memory-results array as completed", () => {
|
||||
expect(parseAddResult([{ id: "m1", memory: "tea" }])).toEqual({
|
||||
status: "completed",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not report a failed event as queued", () => {
|
||||
expect(parseAddResult({ event_id: "evt_1", status: "FAILED" })).toEqual({
|
||||
status: "completed",
|
||||
eventId: "evt_1",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not report an event with no status as queued", () => {
|
||||
expect(parseAddResult({ event_id: "evt_1" })).toEqual({
|
||||
status: "completed",
|
||||
eventId: "evt_1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseSearchHit", () => {
|
||||
it("accepts id, memory_id, nested text, and numeric ids", () => {
|
||||
expect(parseSearchHit({ id: 12, memory: "tea" })).toEqual({
|
||||
@@ -122,18 +159,19 @@ describe("createMem0Store", () => {
|
||||
getAll.mockResolvedValue({
|
||||
results: [{ id: "m9", userId: "scope_abc", metadata: { operation_id: "op_1" } }],
|
||||
});
|
||||
add.mockResolvedValue({ eventId: "evt_1" });
|
||||
add.mockResolvedValue({ event_id: "evt_1", status: "SUCCEEDED" });
|
||||
|
||||
const store = await createMem0Store({
|
||||
apiKey: "m0-test",
|
||||
host: "https://api.mem0.ai",
|
||||
});
|
||||
|
||||
await store.add([{ role: "user", content: "I like tea" }], {
|
||||
const addResult = await store.add([{ role: "user", content: "I like tea" }], {
|
||||
userId: "scope_abc",
|
||||
infer: false,
|
||||
metadata: { source: "eve", operation_id: "op_1" },
|
||||
});
|
||||
expect(addResult).toEqual({ status: "completed", eventId: "evt_1" });
|
||||
const found = await store.search("tea", {
|
||||
userId: "scope_abc",
|
||||
topK: 3,
|
||||
@@ -169,6 +207,21 @@ describe("createMem0Store", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("surfaces a pending platform write as queued", async () => {
|
||||
const store = await createMem0Store({
|
||||
apiKey: "m0-test",
|
||||
host: "https://api.mem0.ai",
|
||||
});
|
||||
add.mockResolvedValueOnce({ event_id: "evt_9", status: "PENDING" });
|
||||
await expect(
|
||||
store.add([{ role: "user", content: "I like tea" }], {
|
||||
userId: "scope_abc",
|
||||
infer: true,
|
||||
metadata: { source: "eve" },
|
||||
}),
|
||||
).resolves.toEqual({ status: "queued", eventId: "evt_9" });
|
||||
});
|
||||
|
||||
it("accepts a bare search array and rejects unknown envelopes", async () => {
|
||||
const store = await createMem0Store({
|
||||
apiKey: "m0-test",
|
||||
|
||||
Reference in New Issue
Block a user