diff --git a/src/resources/extensions/sf/memory-store.ts b/src/resources/extensions/sf/memory-store.ts index 4951b0a21..99e69b16d 100644 --- a/src/resources/extensions/sf/memory-store.ts +++ b/src/resources/extensions/sf/memory-store.ts @@ -434,6 +434,12 @@ export function decayStaleMemories(thresholdUnits = 20): void { /** * Supersede lowest-ranked memories when count exceeds cap. + * + * After superseding, sweeps memory_embeddings for rows whose memory is now + * superseded — keeps the embeddings table aligned with active memories so + * loadAllEmbeddings doesn't carry dead vectors and storage doesn't grow + * unbounded. Best-effort cleanup; the cap enforcement is the load-bearing + * step. */ export function enforceMemoryCap(max = 50): void { if (!isDbAvailable()) return; @@ -451,6 +457,21 @@ export function enforceMemoryCap(max = 50): void { const excess = count - max; supersedeLowestRankedMemories(excess, new Date().toISOString()); + + // Sweep orphaned embeddings for newly-superseded memories. + try { + adapter + .prepare( + `DELETE FROM memory_embeddings WHERE memory_id IN ( + SELECT id FROM memories WHERE superseded_by IS NOT NULL + )`, + ) + .run(); + } catch { + // Orphaned rows are harmless to queries (loadAllEmbeddings filters + // by superseded_by IS NULL); skip-on-error keeps cap enforcement + // load-bearing without coupling to embedding cleanup. + } } catch { // non-fatal }