feat(v2.0.5): Intentional Amnesia — active forgetting via top-down inhibitory control
First AI memory system to model forgetting as a neuroscience-grounded
PROCESS rather than passive decay. Adds the `suppress` MCP tool (#24),
Rac1 cascade worker, migration V10, and dashboard forgetting indicators.
Based on:
- Anderson, Hanslmayr & Quaegebeur (2025), Nat Rev Neurosci — right
lateral PFC as the domain-general inhibitory controller; SIF
compounds with each stopping attempt.
- Cervantes-Sandoval et al. (2020), Front Cell Neurosci PMC7477079 —
Rac1 GTPase as the active synaptic destabilization mechanism.
What's new:
* `suppress` MCP tool — each call compounds `suppression_count` and
subtracts a `0.15 × count` penalty (saturating at 80%) from
retrieval scores during hybrid search. Distinct from delete
(removes) and demote (one-shot).
* Rac1 cascade worker — background sweep piggybacks the 6h
consolidation loop, walks `memory_connections` edges from
recently-suppressed seeds, applies attenuated FSRS decay to
co-activated neighbors. You don't just forget Jake — you fade
the café, the roommate, the birthday.
* 24h labile window — reversible via `suppress({id, reverse: true})`
within 24 hours. Matches Nader reconsolidation semantics.
* Migration V10 — additive-only (`suppression_count`, `suppressed_at`
+ partial indices). All v2.0.x DBs upgrade seamlessly on first launch.
* Dashboard: `ForgettingIndicator.svelte` pulses when suppressions
are active. 3D graph nodes dim to 20% opacity when suppressed.
New WebSocket events: `MemorySuppressed`, `MemoryUnsuppressed`,
`Rac1CascadeSwept`. Heartbeat carries `suppressed_count`.
* Search pipeline: SIF penalty inserted into the accessibility stage
so it stacks on top of passive FSRS decay.
* Tool count bumped 23 → 24. Cognitive modules 29 → 30.
Memories persist — they are INHIBITED, not erased. `memory.get(id)`
returns full content through any number of suppressions. The 24h
labile window is a grace period for regret.
Also fixes issue #31 (dashboard graph view buggy) as a companion UI
bug discovered during the v2.0.5 audit cycle:
* Root cause: node glow `SpriteMaterial` had no `map`, so
`THREE.Sprite` rendered as a solid-coloured 1×1 plane. Additive
blending + `UnrealBloomPass(0.8, 0.4, 0.85)` amplified the square
edges into hard-edged glowing cubes.
* Fix: shared 128×128 radial-gradient `CanvasTexture` singleton used
as the sprite map. Retuned bloom to `(0.55, 0.6, 0.2)`. Halved fog
density (0.008 → 0.0035). Edges bumped from dark navy `0x4a4a7a`
to brand violet `0x8b5cf6` with higher opacity. Added explicit
`scene.background` and a 2000-point starfield for depth.
* 21 regression tests added in `ui-fixes.test.ts` locking every
invariant in (shared texture singleton, depthWrite:false, scale
×6, bloom magic numbers via source regex, starfield presence).
Tests: 1,284 Rust (+47) + 171 Vitest (+21) = 1,455 total, 0 failed
Clippy: clean across all targets, zero warnings
Release binary: 22.6MB, `cargo build --release -p vestige-mcp` green
Versions: workspace aligned at 2.0.5 across all 6 crates/packages
Closes #31
2026-04-14 17:30:30 -05:00
import "../chunks/Bzak7iHL.js" ; import { o as ze } from "../chunks/DeTA_5mp.js" ; import { p as Je , t as w , a as We , d as r , e as t , g as s , s as S , h as m , r as e , O as b , f as Be , u as j } from "../chunks/nyjtQ1Ok.js" ; import { d as He , a as X , s as p } from "../chunks/C4L78yoI.js" ; import { i as u } from "../chunks/B17metm1.js" ; import { e as Z , i as ee } from "../chunks/BilMa3tw.js" ; import { a as v , f as l , t as ue } from "../chunks/B0IenmM-.js" ; import { s as ge } from "../chunks/BgOFZ9jq.js" ; import { s as fe } from "../chunks/D2QmVWrb.js" ; import { s as Le , a as te } from "../chunks/C3ZC25l2.js" ; import { a as O } from "../chunks/BcuCGYSa.js" ; import { m as Ue , a as Ye , i as qe } from "../chunks/B6fk3AxI.js" ; var Qe = l ( '<span class="w-3 h-3 border border-warning/50 border-t-warning rounded-full animate-spin"></span> Running...' , 1 ) , Xe = l ( '<div><div class="text-lg text-text font-semibold"> </div> <div class="text-[10px] text-muted">Processed</div></div>' ) , Ze = l ( '<div><div class="text-lg text-decay font-semibold"> </div> <div class="text-[10px] text-muted">Decayed</div></div>' ) , et = l ( '<div><div class="text-lg text-synapse-glow font-semibold"> </div> <div class="text-[10px] text-muted">Embedded</div></div>' ) , tt = l ( '<div class="bg-white/[0.02] p-3 rounded-lg border border-synapse/10"><div class="grid grid-cols-3 gap-3 text-center"><!> <!> <!></div></div>' ) , st = l ( '<span class="w-3 h-3 border border-dream/50 border-t-dream rounded-full animate-spin"></span> Dreaming...' , 1 ) , at = l ( '<div class="text-xs text-dim bg-dream/5 border border-dream/10 rounded-lg p-2"> </div>' ) , it = l ( '<div class="text-xs text-bright font-medium">Insights Discovered:</div> <!>' , 1 ) , dt = l ( '<div class="text-xs text-dim">Connections found: <span class="text-dream-glow"> </span></div>' ) , rt = l ( '<div class="text-xs text-dim">Memories replayed: <span class="text-text"> </span></div>' ) , ot = l ( '<div class="bg-white/[0.02] p-3 rounded-lg border border-synapse/10 space-y-2"><!> <!> <!></div>' ) , nt = l ( '<div class="flex-1 flex flex-col items-center gap-1"><div class="text-[9px] text-muted"> </div> <div class="w-full rounded-t transition-all duration-500"></div> <div class="text-[9px] text-muted"></div></div>' ) , vt = l ( '<div class="flex items-end gap-1 h-32"></div>' ) , lt = l ( '<section class="space-y-4"><h2 class="text-sm text-bright font-semibold flex items-center gap-2"><span class="text-recall">◫</span> Retention Distribution</h2> <div class="p-4 glass rounded-xl"><!></div></section>' ) , ct = l ( '<div class="flex items-center gap-2 py-1"><kbd class="px-1.5 py-0.5 bg-white/[0.04] rounded text-[10px] font-mono text-muted min-w-[2rem] text-center"> </kbd> <span class="text-dim"> </span></div>' ) , xt = l ( ` <div class="p-6 max-w-4xl mx-auto space-y-8"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Settings & System</h1> <button class="text-xs text-dim hover:text-text transition">Refresh</button></div> <div class="grid grid-cols-2 md:grid-cols-4 gap-3"><div class="p-4 glass rounded-xl text-center"><div class="text-2xl text-bright font-bold"> </div> <div class="text-xs text-dim mt-1">Memories</div></div> <div class="p-4 glass rounded-xl text-center"><div class="text-2xl font-bold"> </div> <div class="text-xs text-dim mt-1">Avg Retention</div></div> <div class="p-4 glass rounded-xl text-center"><div class="text-2xl text-bright font-bold flex items-center justify-center gap-2"><div></div> <span class="text-sm"> </span></div> <div class="text-xs text-dim mt-1">WebSocket</div></div> <div class="p-4 glass rounded-xl text-center"><div class="text-2xl text-synapse-glow font-bold">v2.1</div> <div class="text-xs text-dim mt-1">Vestige</div></div></div> <section class="space-y-4"><h2 class="text-sm text-bright font-semibold flex items-center gap-2"><span class="text-dream">◈</span> Cognitive Operations</h2> <div class="p-4 glass rounded-xl space-y-3"><div class="flex items-center justify-between"><div><div class="text-sm text-text font-medium">FSRS-6 Consolidation</div> <div class="text-xs text-dim">Apply spaced-repetition decay, regenerate embeddings, run maintenance</div></div> <button class="px-4 py-2 bg-warning/20 border border-warning/40
$ { s ( A ) ? "glow-dream animate-pulse-glow" : "" } ` )},[()=>(P()*100).toFixed(1)]),X("click",Re,E),X("click",N,ke),X("click",D,Ce),v(_e,K),We(),he()}He(["click"]);export{Ct as component};