feat: Vestige v2.0.0 "Cognitive Leap" — 3D dashboard, HyDE search, WebSocket events

The biggest release in Vestige history. Complete visual and cognitive overhaul.

Dashboard:
- SvelteKit 2 + Three.js 3D neural visualization at localhost:3927/dashboard
- 7 interactive pages: Graph, Memories, Timeline, Feed, Explore, Intentions, Stats
- WebSocket event bus with 16 event types, real-time 3D animations
- Bloom post-processing, GPU instanced rendering, force-directed layout
- Dream visualization mode, FSRS retention curves, command palette (Cmd+K)
- Keyboard shortcuts, responsive mobile layout, PWA installable
- Single binary deployment via include_dir! (22MB)

Engine:
- HyDE query expansion (intent classification + 3-5 semantic variants + centroid)
- fastembed 5.11 with optional Nomic v2 MoE + Qwen3 reranker + Metal GPU
- Emotional memory module (#29)
- Criterion benchmark suite

Backend:
- Axum WebSocket at /ws with heartbeat + event broadcast
- 7 new REST endpoints for cognitive operations
- Event emission from MCP tools via shared broadcast channel
- CORS for SvelteKit dev mode

Distribution:
- GitHub issue templates (bug report, feature request)
- CHANGELOG with comprehensive v2.0 release notes
- README updated with dashboard docs, architecture diagram, comparison table

734 tests passing, zero warnings, 22MB release binary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-02-22 03:07:25 -06:00
parent 26cee040a5
commit c2d28f3433
321 changed files with 32695 additions and 4727 deletions

View file

@ -0,0 +1,86 @@
import type {
MemoryListResponse,
Memory,
SearchResult,
SystemStats,
HealthCheck,
TimelineResponse,
GraphResponse,
DreamResult,
ImportanceScore,
RetentionDistribution,
ConsolidationResult,
IntentionItem
} from '$types';
const BASE = '/api';
async function fetcher<T>(path: string, options?: RequestInit): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
headers: { 'Content-Type': 'application/json' },
...options
});
if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);
return res.json();
}
export const api = {
// Memories
memories: {
list: (params?: Record<string, string>) => {
const qs = params ? '?' + new URLSearchParams(params).toString() : '';
return fetcher<MemoryListResponse>(`/memories${qs}`);
},
get: (id: string) => fetcher<Memory>(`/memories/${id}`),
delete: (id: string) => fetcher<{ deleted: boolean }>(`/memories/${id}`, { method: 'DELETE' }),
promote: (id: string) => fetcher<Memory>(`/memories/${id}/promote`, { method: 'POST' }),
demote: (id: string) => fetcher<Memory>(`/memories/${id}/demote`, { method: 'POST' })
},
// Search
search: (q: string, limit = 20) =>
fetcher<SearchResult>(`/search?q=${encodeURIComponent(q)}&limit=${limit}`),
// Stats & Health
stats: () => fetcher<SystemStats>('/stats'),
health: () => fetcher<HealthCheck>('/health'),
// Timeline
timeline: (days = 7, limit = 200) =>
fetcher<TimelineResponse>(`/timeline?days=${days}&limit=${limit}`),
// Graph
graph: (params?: { query?: string; center_id?: string; depth?: number; max_nodes?: number }) => {
const qs = params ? '?' + new URLSearchParams(
Object.entries(params)
.filter(([, v]) => v !== undefined)
.map(([k, v]) => [k, String(v)])
).toString() : '';
return fetcher<GraphResponse>(`/graph${qs}`);
},
// Cognitive operations
dream: () => fetcher<DreamResult>('/dream', { method: 'POST' }),
explore: (fromId: string, action = 'associations', toId?: string, limit = 10) =>
fetcher<Record<string, unknown>>('/explore', {
method: 'POST',
body: JSON.stringify({ from_id: fromId, action, to_id: toId, limit })
}),
predict: () => fetcher<Record<string, unknown>>('/predict', { method: 'POST' }),
importance: (content: string) =>
fetcher<ImportanceScore>('/importance', {
method: 'POST',
body: JSON.stringify({ content })
}),
consolidate: () => fetcher<ConsolidationResult>('/consolidate', { method: 'POST' }),
retentionDistribution: () => fetcher<RetentionDistribution>('/retention-distribution'),
// Intentions
intentions: (status = 'active') =>
fetcher<{ intentions: IntentionItem[]; total: number; filter: string }>(`/intentions?status=${status}`)
};

View file

@ -0,0 +1,103 @@
import { writable, derived } from 'svelte/store';
import type { VestigeEvent } from '$types';
const MAX_EVENTS = 200;
function createWebSocketStore() {
const { subscribe, set, update } = writable<{
connected: boolean;
events: VestigeEvent[];
lastHeartbeat: VestigeEvent | null;
error: string | null;
}>({
connected: false,
events: [],
lastHeartbeat: null,
error: null
});
let ws: WebSocket | null = null;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
let reconnectAttempts = 0;
function connect(url?: string) {
const wsUrl = url || (window.location.port === '5173'
? `ws://${window.location.hostname}:3927/ws`
: `ws://${window.location.host}/ws`);
if (ws?.readyState === WebSocket.OPEN) return;
try {
ws = new WebSocket(wsUrl);
ws.onopen = () => {
reconnectAttempts = 0;
update(s => ({ ...s, connected: true, error: null }));
};
ws.onmessage = (event) => {
try {
const parsed: VestigeEvent = JSON.parse(event.data);
update(s => {
if (parsed.type === 'Heartbeat') {
return { ...s, lastHeartbeat: parsed };
}
const events = [parsed, ...s.events].slice(0, MAX_EVENTS);
return { ...s, events };
});
} catch {
// Ignore malformed messages
}
};
ws.onclose = () => {
update(s => ({ ...s, connected: false }));
scheduleReconnect(wsUrl);
};
ws.onerror = () => {
update(s => ({ ...s, error: 'WebSocket connection failed' }));
};
} catch (e) {
update(s => ({ ...s, error: String(e) }));
}
}
function scheduleReconnect(url: string) {
if (reconnectTimer) clearTimeout(reconnectTimer);
const delay = Math.min(1000 * 2 ** reconnectAttempts, 30000);
reconnectAttempts++;
reconnectTimer = setTimeout(() => connect(url), delay);
}
function disconnect() {
if (reconnectTimer) clearTimeout(reconnectTimer);
ws?.close();
ws = null;
set({ connected: false, events: [], lastHeartbeat: null, error: null });
}
function clearEvents() {
update(s => ({ ...s, events: [] }));
}
return {
subscribe,
connect,
disconnect,
clearEvents
};
}
export const websocket = createWebSocketStore();
// Derived stores for specific event types
export const isConnected = derived(websocket, $ws => $ws.connected);
export const eventFeed = derived(websocket, $ws => $ws.events);
export const heartbeat = derived(websocket, $ws => $ws.lastHeartbeat);
export const memoryCount = derived(websocket, $ws =>
($ws.lastHeartbeat?.data?.memory_count as number) ?? 0
);
export const avgRetention = derived(websocket, $ws =>
($ws.lastHeartbeat?.data?.avg_retention as number) ?? 0
);