mirror of
https://github.com/samvallad33/vestige.git
synced 2026-07-04 22:02:14 +02:00
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:
parent
26cee040a5
commit
c2d28f3433
321 changed files with 32695 additions and 4727 deletions
103
apps/dashboard/src/lib/stores/websocket.ts
Normal file
103
apps/dashboard/src/lib/stores/websocket.ts
Normal 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
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue