diff --git a/extensions/memory-core/src/short-term-promotion.test.ts b/extensions/memory-core/src/short-term-promotion.test.ts index 78580425f8e2..d36ed11c8ab8 100644 --- a/extensions/memory-core/src/short-term-promotion.test.ts +++ b/extensions/memory-core/src/short-term-promotion.test.ts @@ -459,6 +459,71 @@ describe("short-term promotion", () => { }); }); + it("ignores raw session and transcript snippets when recording short-term recalls", async () => { + await withTempWorkspace(async (workspaceDir) => { + await recordShortTermRecalls({ + workspaceDir, + query: "session recap", + results: [ + { + path: "memory/2026-06-18.md", + source: "memory", + startLine: 1, + endLine: 1, + score: 0.92, + snippet: + "Session: 2026-06-18 10:37:05 EDT; Session Key: agent:cody:discord:channel:1502199757592989836; Session ID: 6d52b6a2-a2e1-4839-a69a-a532b9090a6d; Source: discord", + }, + { + path: "memory/2026-06-18.md", + source: "memory", + startLine: 2, + endLine: 2, + score: 0.91, + snippet: "Conversation Summary: assistant: Traced all three. No changes made.", + }, + { + path: "memory/2026-06-18.md", + source: "memory", + startLine: 3, + endLine: 3, + score: 0.9, + snippet: + "user: Save important context from this session to the daily memory file. STRICT RULES: 1. The file MUST be named exactly memory/2026-06-18.md", + }, + ], + }); + + const store = await testing.readRecallStore(workspaceDir, new Date().toISOString()); + expect(store.version).toBe(1); + expect(store.entries).toEqual({}); + }); + }); + + it("ignores already-promoted score metadata snippets when recording short-term recalls", async () => { + await withTempWorkspace(async (workspaceDir) => { + await recordShortTermRecalls({ + workspaceDir, + query: "promotion metadata", + results: [ + { + path: "memory/2026-06-18.md", + source: "memory", + startLine: 1, + endLine: 1, + score: 0.94, + snippet: + "2026-06-13 09:20 America/New_York - Polycore PR #112 re-review... [score=0.837 recalls=0 avg=0.620 source=memory/2026-06-13.md:10-12]", + }, + ], + }); + + const store = await testing.readRecallStore(workspaceDir, new Date().toISOString()); + expect(store.version).toBe(1); + expect(store.entries).toEqual({}); + }); + }); + it("keeps ordinary snippets that only quote dreaming prompt markers", async () => { await withTempWorkspace(async (workspaceDir) => { await recordShortTermRecalls({ @@ -1595,6 +1660,38 @@ describe("short-term promotion", () => { ).toBe(true); }); + it("treats raw session metadata snippets as contaminated", () => { + expect( + testing.isContaminatedDreamingSnippet( + "Session: 2026-06-18 10:37:05 EDT; Session Key: agent:cody:discord:channel:1502199757592989836; Session ID: 6d52b6a2-a2e1-4839-a69a-a532b9090a6d; Source: discord", + ), + ).toBe(true); + }); + + it("treats raw conversation summaries as contaminated", () => { + expect( + testing.isContaminatedDreamingSnippet( + "Conversation Summary: assistant: Traced all three. No changes made.", + ), + ).toBe(true); + }); + + it("treats raw transcript turns as contaminated", () => { + expect( + testing.isContaminatedDreamingSnippet( + "user: Save important context from this session to the daily memory file. STRICT RULES: 1. The file MUST be named exactly memory/2026-06-18.md", + ), + ).toBe(true); + }); + + it("treats promotion score metadata as contaminated", () => { + expect( + testing.isContaminatedDreamingSnippet( + "Polycore PR #112 re-review... [score=0.837 recalls=0 avg=0.620 source=memory/2026-06-13.md:10-12]", + ), + ).toBe(true); + }); + it("does not treat prose that mentions the word Candidate as contaminated", () => { expect( testing.isContaminatedDreamingSnippet( diff --git a/extensions/memory-core/src/short-term-promotion.ts b/extensions/memory-core/src/short-term-promotion.ts index 052eac94a339..1d1680b46758 100644 --- a/extensions/memory-core/src/short-term-promotion.ts +++ b/extensions/memory-core/src/short-term-promotion.ts @@ -74,6 +74,14 @@ const PHASE_SIGNAL_REM_BOOST_MAX = 0.09; const PHASE_SIGNAL_HALF_LIFE_DAYS = 14; const DREAMING_TRANSCRIPT_PROMPT_LINE_RE = /\[[^\]]*dreaming-narrative[^\]]*]\s*(?:User|Assistant):\s*Write a dream diary entry from these memory fragments:?/i; +const RAW_SESSION_METADATA_RE = + /\bSession Key\b.{0,260}\bSession ID\b|\bSession ID\b.{0,260}\bSession Key\b/i; +const RAW_CONVERSATION_SUMMARY_RE = /^(?:[-*+]\s*)?Conversation Summary:/i; +const RAW_TRANSCRIPT_TURN_RE = /^(?:[-*+]\s*)?(?:user|assistant):\s/i; +const MEMORY_FLUSH_PROMPT_RE = + /Save important context from this session to the daily memory file\.\s*STRICT RULES:/i; +const PROMOTION_SCORE_METADATA_RE = + /\[\s*score=\d+(?:\.\d+)?\s+recalls=\d+\s+avg=\d+(?:\.\d+)?\s+source=memory\//i; const DREAMING_DIFF_PREFIX_RE = /@@\s*-\d+(?:,\d+)?\s+[-*+]\s+/iy; const GENERIC_DAY_HEADING_RE = /^(?:(?:mon|monday|tue|tues|tuesday|wed|wednesday|thu|thur|thurs|thursday|fri|friday|sat|saturday|sun|sunday)(?:,\s+)?)?(?:(?:jan|january|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|sept|september|oct|october|nov|november|dec|december)\s+\d{1,2}(?:st|nd|rd|th)?(?:,\s*\d{4})?|\d{1,2}[/-]\d{1,2}(?:[/-]\d{2,4})?|\d{4}[/-]\d{2}[/-]\d{2})$/i; @@ -409,7 +417,12 @@ function isContaminatedDreamingSnippet(raw: string): boolean { } if ( /