vestige/apps/dashboard/build/_app/immutable/nodes/7.404BTmXF.js
Sam Valladares 8178beb961 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

1 line
6 KiB
JavaScript

import"../chunks/Bzak7iHL.js";import{o as ut}from"../chunks/DeTA_5mp.js";import{p as gt,s as T,c as Q,t as u,a as ft,d,e as s,h as $,g as t,r as e,O as ht}from"../chunks/nyjtQ1Ok.js";import{d as bt,s as l,a as yt}from"../chunks/C4L78yoI.js";import{i as R}from"../chunks/B17metm1.js";import{e as U,i as D}from"../chunks/BilMa3tw.js";import{a as o,f as n}from"../chunks/B0IenmM-.js";import{s as q}from"../chunks/BgOFZ9jq.js";import{a as Z}from"../chunks/BcuCGYSa.js";var wt=n("<button> </button>"),Rt=n('<div class="h-16 glass-subtle rounded-xl animate-pulse"></div>'),Nt=n('<div class="space-y-2"></div>'),Ot=n('<div class="text-center py-12 text-dim"><div class="text-4xl mb-3 opacity-20">◇</div> <p> </p> <p class="text-xs text-muted mt-1">Use "Remind me..." in conversation to create intentions.</p></div>'),St=n('<span class="text-[10px] text-dream-glow"> </span>'),kt=n('<span class="text-[10px] text-muted"> </span>'),$t=n('<div class="p-4 glass-subtle rounded-xl"><div class="flex items-start gap-3"><div class="w-8 h-8 rounded-lg bg-white/[0.04] flex items-center justify-center text-lg flex-shrink-0"> </div> <div class="flex-1 min-w-0"><p class="text-sm text-text"> </p> <div class="flex flex-wrap gap-2 mt-2"><span> </span> <span> </span> <span class="text-[10px] text-muted"> </span> <!> <!></div></div> <span class="text-[10px] text-muted flex-shrink-0"> </span></div></div>'),zt=n('<div class="space-y-2"></div>'),Ct=n('<div class="text-center py-8 text-dim"><div class="text-3xl mb-3 opacity-20">◬</div> <p class="text-sm">No predictions yet. Use Vestige more to train the predictive model.</p></div>'),It=n("<span> </span>"),Pt=n('<span class="text-dream-glow"> </span>'),Tt=n('<div class="p-3 glass-subtle rounded-xl flex items-start gap-3"><div class="w-6 h-6 rounded-full bg-dream/20 text-dream-glow text-xs flex items-center justify-center flex-shrink-0 mt-0.5"></div> <div class="flex-1 min-w-0"><p class="text-sm text-text line-clamp-2"> </p> <div class="flex gap-3 mt-1 text-xs text-muted"><span> </span> <!> <!></div></div></div>'),Ut=n('<div class="space-y-2"></div>'),Dt=n('<div class="p-6 max-w-5xl mx-auto space-y-8"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Intentions & Predictions</h1> <span class="text-xs text-muted"> </span></div> <div class="space-y-4"><div class="flex items-center gap-2"><h2 class="text-sm text-bright font-semibold">Prospective Memory</h2> <span class="text-xs text-muted">"Remember to do X when Y happens"</span></div> <div class="flex gap-1.5"></div> <!></div> <div class="pt-6 border-t border-synapse/10 space-y-4"><div class="flex items-center gap-2"><h2 class="text-sm text-bright font-semibold">Predicted Needs</h2> <span class="text-xs text-muted">What you might need next</span></div> <!></div></div>');function Wt(tt,et){gt(et,!0);let z=T(Q([])),j=T(Q([])),A=T(!0),N=T("active");const at={active:"text-synapse-glow bg-synapse/10 border-synapse/30",fulfilled:"text-recall bg-recall/10 border-recall/30",cancelled:"text-dim bg-white/[0.03] border-subtle/20",snoozed:"text-dream-glow bg-dream/10 border-dream/30"},st={critical:"text-decay",high:"text-amber-400",normal:"text-dim",low:"text-muted"},rt={time:"⏰",context:"◎",event:"⚡"};ut(async()=>{await B()});async function B(){$(A,!0);try{const[r,i]=await Promise.all([Z.intentions(t(N)),Z.predict()]);$(z,r.intentions||[],!0),$(j,i.predictions||[],!0)}catch{}finally{$(A,!1)}}async function it(r){$(N,r,!0),await B()}function F(r){if(!r)return"";try{return new Date(r).toLocaleDateString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return r}}var L=Dt(),G=s(L),H=d(s(G),2),dt=s(H);e(H),e(G);var M=d(G,2),Y=d(s(M),2);U(Y,20,()=>["active","fulfilled","snoozed","cancelled","all"],D,(r,i)=>{var v=wt(),a=s(v,!0);e(v),u(x=>{q(v,1,`px-3 py-1.5 rounded-xl text-xs transition ${t(N)===i?"bg-synapse/20 text-synapse-glow border border-synapse/40":"glass-subtle text-dim hover:bg-white/[0.03]"}`),l(a,x)},[()=>i.charAt(0).toUpperCase()+i.slice(1)]),yt("click",v,()=>it(i)),o(r,v)}),e(Y);var lt=d(Y,2);{var vt=r=>{var i=Nt();U(i,20,()=>Array(4),D,(v,a)=>{var x=Rt();o(v,x)}),e(i),o(r,i)},ot=r=>{var i=Ot(),v=d(s(i),2),a=s(v);e(v),ht(2),e(i),u(()=>l(a,`No ${t(N)==="all"?"":t(N)+" "}intentions.`)),o(r,i)},nt=r=>{var i=zt();U(i,21,()=>t(z),D,(v,a)=>{var x=$t(),f=s(x),h=s(f),C=s(h,!0);e(h);var g=d(h,2),O=s(g),I=s(O,!0);e(O);var b=d(O,2),y=s(b),P=s(y,!0);e(y);var w=d(y,2),E=s(w);e(w);var S=d(w,2),c=s(S);e(S);var p=d(S,2);{var k=m=>{var _=St(),W=s(_);e(_),u(X=>l(W,`deadline: ${X??""}`),[()=>F(t(a).deadline)]),o(m,_)};R(p,m=>{t(a).deadline&&m(k)})}var V=d(p,2);{var mt=m=>{var _=kt(),W=s(_);e(_),u(X=>l(W,`snoozed until ${X??""}`),[()=>F(t(a).snoozed_until)]),o(m,_)};R(V,m=>{t(a).snoozed_until&&m(mt)})}e(b),e(g);var K=d(g,2),_t=s(K,!0);e(K),e(f),e(x),u((m,_)=>{l(C,rt[t(a).trigger_type]||"◇"),l(I,t(a).content),q(y,1,`px-2 py-0.5 text-[10px] rounded-lg border ${(at[t(a).status]||"text-dim bg-white/[0.03] border-subtle/20")??""}`),l(P,t(a).status),q(w,1,`text-[10px] ${(st[t(a).priority]||"text-muted")??""}`),l(E,`${t(a).priority??""} priority`),l(c,`${t(a).trigger_type??""}: ${m??""}`),l(_t,_)},[()=>t(a).trigger_value.length>40?t(a).trigger_value.slice(0,37)+"...":t(a).trigger_value,()=>F(t(a).created_at)]),o(v,x)}),e(i),o(r,i)};R(lt,r=>{t(A)?r(vt):t(z).length===0?r(ot,1):r(nt,!1)})}e(M);var J=d(M,2),pt=d(s(J),2);{var xt=r=>{var i=Ct();o(r,i)},ct=r=>{var i=Ut();U(i,21,()=>t(j),D,(v,a,x)=>{var f=Tt(),h=s(f);h.textContent=x+1;var C=d(h,2),g=s(C),O=s(g,!0);e(g);var I=d(g,2),b=s(I),y=s(b,!0);e(b);var P=d(b,2);{var w=c=>{var p=It(),k=s(p);e(p),u(V=>l(k,`${V??""}% retention`),[()=>(Number(t(a).retention)*100).toFixed(0)]),o(c,p)};R(P,c=>{t(a).retention&&c(w)})}var E=d(P,2);{var S=c=>{var p=Pt(),k=s(p);e(p),u(()=>l(k,`${t(a).predictedNeed??""} need`)),o(c,p)};R(E,c=>{t(a).predictedNeed&&c(S)})}e(I),e(C),e(f),u(()=>{l(O,t(a).content),l(y,t(a).nodeType)}),o(v,f)}),e(i),o(r,i)};R(pt,r=>{t(j).length===0?r(xt):r(ct,!1)})}e(J),e(L),u(()=>l(dt,`${t(z).length??""} intentions`)),o(tt,L),ft()}bt(["click"]);export{Wt as component};