From 30b734011186bc16a49fe3cc476e70aa0cc447cd Mon Sep 17 00:00:00 2001 From: compoodment Date: Fri, 12 Jun 2026 20:49:36 +0200 Subject: [PATCH] fix: ignore non-object memory metadata --- src/memory-runtime.ts | 5 ++++- test/unit/memory-runtime.test.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/memory-runtime.ts b/src/memory-runtime.ts index 37d034be..0a5ff8f6 100644 --- a/src/memory-runtime.ts +++ b/src/memory-runtime.ts @@ -282,7 +282,10 @@ function normalizeSearchCorpus(value: unknown): "all" | "memory" | "sessions" { function parseMetadataJson(item: { metadataJson?: Uint8Array }): Record { if (item.metadataJson && item.metadataJson.length > 0) { try { - return JSON.parse(new TextDecoder().decode(item.metadataJson)); + const parsed = JSON.parse(new TextDecoder().decode(item.metadataJson)) as unknown; + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + return parsed as Record; + } } catch (e) { // ignore } diff --git a/test/unit/memory-runtime.test.ts b/test/unit/memory-runtime.test.ts index 3cd19982..ad728c58 100644 --- a/test/unit/memory-runtime.test.ts +++ b/test/unit/memory-runtime.test.ts @@ -206,6 +206,35 @@ test("memory runtime bridge falls back to metadata text when search result text assert.equal(loaded.text, metadataText); }); +test("memory runtime bridge treats non-object metadata JSON as missing", async () => { + const rpc = new FakeRpc(); + (rpc as { searchTextCollections: (params: Record) => Promise<{ results: unknown[] }> }).searchTextCollections = async (params) => { + rpc.calls.push({ method: "searchTextCollections", params }); + return { + results: [ + { + id: "turn-1", + score: 0.88, + text: "remembered item survives non-object metadata", + metadataJson: new TextEncoder().encode("null"), + }, + ], + }; + }; + const runtime = buildMemoryRuntimeBridge(async () => rpc as never, {}); + const { manager } = await runtime.getMemorySearchManager(); + + const result = await manager.search({ query: "remembered item", sessionId: "s1" }) as Array<{ + path: string; + snippet: string; + source: string; + }>; + + assert.equal(result.length, 1); + assert.equal(result[0]?.snippet, "remembered item survives non-object metadata"); + assert.equal(result[0]?.source, "memory"); +}); + test("memory runtime bridge does not authorize hidden paths from legacy search results", async () => { const rpc = new FakeRpc(); const runtime = buildMemoryRuntimeBridge(async () => rpc as never, {});