vestige/apps/dashboard/build/_app/immutable/nodes/5.BgcStf4T.js

9 lines
7.8 KiB
JavaScript
Raw Normal View History

feat(graph): FSRS memory-state colour mode + legend overlay Closes Agent 1's audit gap #4: FSRS memory state (Active / Dormant / Silent / Unavailable) was computed server-side per query but never rendered in the 3D graph. Spheres always tinted by node type. The new colour mode adds a second channel that users can toggle between at runtime — Type (default, existing behaviour) and State (new). The toggle is a radio-pair pill in the graph page's top-right control bar next to the node-count selector + Dream button. Buckets + palette: - Active ≥ 70% emerald #10b981 easily retrievable - Dormant 40-70% amber #f59e0b retrievable with effort - Silent 10-40% violet #8b5cf6 difficult, needs cues - Unavail. < 10% slate #6b7280 needs reinforcement Thresholds match `execute_system_status` at the backend so the graph colour bands line up exactly with what the Stats page reports in its stateDistribution block. Using retention as the proxy for the full accessibility formula (retention × 0.5 + retrieval × 0.3 + storage × 0.2) is an approximation — retention is the dominant 0.5 weight and it is the only FSRS channel the current GraphNode DTO carries. Swap to the full formula in a future release if the DTO grows. Implementation: - `apps/dashboard/src/lib/graph/nodes.ts` — new `MemoryState` type, `getMemoryState(retention)`, `MEMORY_STATE_COLORS`, `MEMORY_STATE_DESCRIPTIONS`, `ColorMode`, `getNodeColor(node, mode)`. - `NodeManager.colorMode` field (default `'type'`). `createNodeMeshes` now calls `getNodeColor(node, this.colorMode)` so newly-added nodes during the session follow the toggled mode. - New `NodeManager.setColorMode(mode)` mutates every live mesh's material + glow sprite colour in place. Idempotent; cheap. Does NOT touch opacity/emissive-intensity so the v2.0.5 suppression dimming layer keeps working unchanged. - New `MemoryStateLegend.svelte` floating overlay in the bottom-right when state mode is active (hidden in type mode so the legend doesn't compete with the node-type palette). - `Graph3D.svelte` accepts a new `colorMode` prop (default `'type'`) and runs a `$effect` that calls `setColorMode` on every toggle. - Dashboard rebuild picks up the new component + wiring. Tests: 171 vitest, svelte-check 581 files / 0 errors. No backend changes; this is pure dashboard code.
2026-04-19 20:45:08 -05:00
import"../chunks/Bzak7iHL.js";import"../chunks/CrlWs-6R.js";import{p as ee,I as oe,g as e,h as M,e as o,d,r as s,f as ie,t as $,a as te,s as K,u as X,C as Z}from"../chunks/VE8Jor13.js";import{s as x,d as ne,a as de}from"../chunks/DHnEMX8z.js";import{a as l,f as m}from"../chunks/7UNxJI5L.js";import{i as w}from"../chunks/JkhlGLjU.js";import{e as re,i as ae}from"../chunks/ByItJEsC.js";import{s as C}from"../chunks/ussr1V5_.js";import{i as ce}from"../chunks/jyeIy8pa.js";import{s as le,a as me}from"../chunks/AcZBvMXu.js";import{w as ve,e as ue}from"../chunks/XIUN5r_Y.js";import{E as I}from"../chunks/BNytumrp.js";import{s as pe}from"../chunks/Cu3VmnGp.js";import{s as fe}from"../chunks/BR2EHpd7.js";import{p as Q}from"../chunks/ykT2B6d3.js";var xe=m('<span class="text-[10px] text-recall"> </span>'),_e=m('<div class="h-px flex-shrink-0 w-2 mt-[-12px] transition-all duration-300"></div>'),ge=m('<div class="flex flex-col items-center gap-1 flex-1 min-w-0"><div> </div> <span class="text-[8px] truncate w-full text-center transition-colors duration-300"> </span></div> <!>',1),he=m('<div class="h-full rounded-full transition-all ease-out"></div>'),ye=m('<div class="flex items-center gap-2 pt-1 animate-fade-in svelte-1n9p0vm"><div class="w-1.5 h-1.5 rounded-full bg-recall animate-pulse-glow"></div> <span class="text-[10px] text-dim"> </span></div>'),$e=m('<div class="glass-subtle rounded-xl p-4 space-y-3"><div class="flex items-center justify-between"><span class="text-[10px] text-synapse-glow uppercase tracking-wider font-medium">Cognitive Search Pipeline</span> <!></div> <div class="flex items-center gap-0.5"></div> <div class="h-1 bg-white/[0.03] rounded-full overflow-hidden"><!></div> <!></div>');function be(O,F){ee(F,!0);let S=Q(F,"resultCount",3,0),j=Q(F,"durationMs",3,0),q=Q(F,"active",3,!1);const p=[{name:"Overfetch",icon:"◎",color:"#818CF8",desc:"Pull 3x results from hybrid search"},{name:"Rerank",icon:"⟿",color:"#00A8FF",desc:"Re-score by relevance quality"},{name:"Temporal",icon:"◷",color:"#00D4FF",desc:"Recent memories get recency bonus"},{name:"Access",icon:"◇",color:"#00FFD1",desc:"FSRS-6 retention threshold filter"},{name:"Context",icon:"◬",color:"#FFB800",desc:"Encoding specificity matching"},{name:"Compete",icon:"⬡",color:"#FF3CAC",desc:"Retrieval-induced forgetting"},{name:"Activate",icon:"◈",color:"#9D00FF",desc:"Spreading activation cascade"}];let _=K(-1),g=K(!1),u=K(!1);oe(()=>{q()&&!e(g)&&P()});function P(){M(g,!0),M(_,-1),M(u,!1);const t=Math.max(1500,(j()||50)*2),a=t/(p.length+1);p.forEach((i,v)=>{setTimeout(()=>{M(_,v,!0)},a*(v+1))}),setTimeout(()=>{M(u,!0),M(g,!1)},t)}var D=$e(),b=o(D),L=d(o(b),2);{var V=t=>{var a=xe(),i=o(a);s(a),$(()=>x(i,`${S()??""} results in ${j()??""}ms`)),l(t,a)};w(L,t=>{e(u)&&t(V)})}s(b);var A=d(b,2);re(A,21,()=>p,ae,(t,a,i)=>{const v=X(()=>i<=e(_)),E=X(()=>i===e(_)&&e(g));var k=ge(),h=ie(k),y=o(h),J=o(y,!0);s(y);var R=d(y,2),T=o(R,!0);s(R),s(h);var U=d(h,2);{var W=B=>{var c=_e();$(()=>C(c,`background: ${i<e(_)?p[i+1].color+"60":"rgba(255,255,255,0.06)"}`)),l(B,c)};w(U,B=>{i<p.length-1&&B(W)})}$(()=>{fe(y,1,`w-8 h-8 rounded-full flex items-center justify-center text-xs transition-all duration-300
${e(E)?"scale-125":""}`),C(y,`background: ${e(v)?e(a).color+"25":"rgba(255,255,255,0.03)"};
border: 1.5px solid ${(e(v)?e(a).color:"rgba(255,255,255,0.06)")??""};
color: ${(e(v)?e(a).color:"#4a4a7a")??""};
box-shadow: ${e(E)?"0 0 12px "+e(a).color+"40":"none"}`),pe(y,"title",e(a).desc),x(J,e(a).icon),C(R,`color: ${(e(v)?e(a).color:"#4a4a7a")??""}`),x(T,e(a).name)}),l(t,k)}),s(A);var N=d(A,2),z=o(N);{var n=t=>{var a=he();$(i=>C(a,`width: ${i??""}%;
background: linear-gradient(90deg, #818CF8, #00FFD1, #9D00FF);
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
transition-duration: ${e(g)?"300ms":"500ms"}`),[()=>e(u)?"100":((e(_)+1)/p.length*100).toFixed(0)]),l(t,a)};w(z,t=>{(e(g)||e(u))&&t(n)})}s(N);var r=d(N,2);{var H=t=>{var a=ye(),i=d(o(a),2),v=o(i);s(i),s(a),$(()=>x(v,`Pipeline complete: ${S()??""} memories surfaced from ${p.length??""}-stage cognitive cascade`)),l(t,a)};w(r,t=>{e(u)&&t(H)})}s(D),l(O,D),te()}var we=m('<div class="text-center py-20 text-dim"><div class="text-4xl mb-4">◉</div> <p>Waiting for cognitive events...</p> <p class="text-sm text-muted mt-2">Events appear here in real-time as Vestige thinks.</p></div>'),Ce=m('<span class="text-xs text-muted"> </span>'),Fe=m('<div class="mt-2"><!></div>'),Se=m(`<div class="flex items-start gap-3 p-3 glass-subtle rounded-xl
feat(graph): FSRS memory-state colour mode + legend overlay Closes Agent 1's audit gap #4: FSRS memory state (Active / Dormant / Silent / Unavailable) was computed server-side per query but never rendered in the 3D graph. Spheres always tinted by node type. The new colour mode adds a second channel that users can toggle between at runtime — Type (default, existing behaviour) and State (new). The toggle is a radio-pair pill in the graph page's top-right control bar next to the node-count selector + Dream button. Buckets + palette: - Active ≥ 70% emerald #10b981 easily retrievable - Dormant 40-70% amber #f59e0b retrievable with effort - Silent 10-40% violet #8b5cf6 difficult, needs cues - Unavail. < 10% slate #6b7280 needs reinforcement Thresholds match `execute_system_status` at the backend so the graph colour bands line up exactly with what the Stats page reports in its stateDistribution block. Using retention as the proxy for the full accessibility formula (retention × 0.5 + retrieval × 0.3 + storage × 0.2) is an approximation — retention is the dominant 0.5 weight and it is the only FSRS channel the current GraphNode DTO carries. Swap to the full formula in a future release if the DTO grows. Implementation: - `apps/dashboard/src/lib/graph/nodes.ts` — new `MemoryState` type, `getMemoryState(retention)`, `MEMORY_STATE_COLORS`, `MEMORY_STATE_DESCRIPTIONS`, `ColorMode`, `getNodeColor(node, mode)`. - `NodeManager.colorMode` field (default `'type'`). `createNodeMeshes` now calls `getNodeColor(node, this.colorMode)` so newly-added nodes during the session follow the toggled mode. - New `NodeManager.setColorMode(mode)` mutates every live mesh's material + glow sprite colour in place. Idempotent; cheap. Does NOT touch opacity/emissive-intensity so the v2.0.5 suppression dimming layer keeps working unchanged. - New `MemoryStateLegend.svelte` floating overlay in the bottom-right when state mode is active (hidden in type mode so the legend doesn't compete with the node-type palette). - `Graph3D.svelte` accepts a new `colorMode` prop (default `'type'`) and runs a `$effect` that calls `setColorMode` on every toggle. - Dashboard rebuild picks up the new component + wiring. Tests: 171 vitest, svelte-check 581 files / 0 errors. No backend changes; this is pure dashboard code.
2026-04-19 20:45:08 -05:00
hover:bg-white/[0.03] transition-all duration-200"><div class="w-6 h-6 rounded flex items-center justify-center text-xs flex-shrink-0"> </div> <div class="flex-1 min-w-0"><div class="flex items-center gap-2 mb-0.5"><span class="text-xs font-medium"> </span> <!></div> <p class="text-sm text-dim"> </p> <!></div></div>`),De=m('<div class="space-y-2"></div>'),ke=m('<div class="p-6 max-w-4xl mx-auto space-y-6"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Live Feed</h1> <div class="flex gap-3"><span class="text-dim text-sm"> </span> <button class="text-xs text-muted hover:text-text transition">Clear</button></div></div> <!></div>');function He(O,F){ee(F,!1);const S=()=>me(ue,"$eventFeed",j),[j,q]=le();function p(n){return new Date(n).toLocaleTimeString()}function _(n){return{MemoryCreated:"+",MemoryUpdated:"~",MemoryDeleted:"×",MemoryPromoted:"",MemoryDemoted:"",SearchPerformed:"",DreamStarted:"",DreamProgress:"",DreamCompleted:"",ConsolidationStarted:"",ConsolidationCompleted:"",RetentionDecayed:"",ConnectionDiscovered:"",ActivationSpread:"",ImportanceScored:"",Heartbeat:""}[n]||"·"}function g(n){const r=n.data;switch(n.type){case"MemoryCreated":return`New ${r.node_type}: "${String(r.content_preview).slice(0,60)}..."`;case"SearchPerformed":return`Searched "${r.query}" → ${r.result_count} results (${r.duration_ms}ms)`;case"DreamStarted":return`Dream started with ${r.memory_count} memories`;case"DreamCompleted":return`Dream complete: ${r.connections_found} connections, ${r.insights_generated} insights (${r.duration_ms}ms)`;case"ConsolidationStarted":return"Consolidation cycle started";case"ConsolidationCompleted":return`Consolidated ${r.nodes_processed} nodes, ${r.decay_applied} decayed (${r.duration_ms}ms)`;case"ConnectionDiscovered":return`Connection: ${String(r.connection_type)} (weight: ${Number(r.weight).toFixed(2)})`;case"ImportanceScored":return`Scored ${Number(r.composite_score).toFixed(2)}: "${String(r.content_preview).slice(0,50)}..."`;case"MemoryPromoted":return`Promoted → ${(Number(r.new_retention)*100).toFixed(0)}% retention`;case"MemoryDemoted":return`Demoted → ${(Number(r.new_retention)*100).toFixed(0)}% retention`;default:return JSON.stringify(r).slice(0,100)}}ce();var u=ke(),P=o(u),D=d(o(P),2),b=o(D),L=o(b);s(b);var V=d(b,2);s(D),s(P);var A=d(P,2);{var N=n=>{var r=we();l(n,r)},z=n=>{var r=De();re(r,5,S,ae,(H,t)=>{var a=Se(),i=o(a),v=o(i,!0);s(i);var E=d(i,2),k=o(E),h=o(k),y=o(h,!0);s(h);var J=d(h,2);{var R=c=>{var f=Ce(),Y=o(f,!0);s(f),$(G=>x(Y,G),[()=>p(String(e(t).data.timestamp))]),l(c,f)};w(J,c=>{e(t).data.timestamp&&c(R)})}s(k);var T=d(k,2),U=o(T,!0);s(T);var W=d(T,2);{var B=c=>{var f=Fe(),Y=o(f);{let G=Z(()=>Number(e(t).data.result_count)||0),se=Z(()=>Number(e(t).data.duration_ms)||0);be(Y,{get resultCount(){return e(G)},get durationMs(){return e(se)},active:!0})}s(f),l(c,f)};w(W,c=>{e(t).type==="SearchPerformed"&&c(B)})}s(E),s(a),$((c,f)=>{C(a,`border-left: 3px solid ${(I[e(t).type]||"#8B95A5")??""}`),C(i,`background: ${(I[e(t).type]||"#8B95A5")??""}15; color: ${(I[e(t).type]||"#8B95A5")??""}`),x(v,c),C(h,`color: ${(I[e(t).type]||"#8B95A5")??""}`),x(y,e(t).type),x(U,f)},[()=>_(e(t).type),()=>g(e(t))]),l(H,a)}),s(r),l(n,r)};w(A,n=>{S().length===0?n(N):n(z,!1)})}s(u),$(()=>x(L,`${S().length??""} events`)),de("click",V,()=>ve.clearEvents()),l(O,u),te(),q()}ne(["click"]);export{He as component};