vestige/apps/dashboard/src/lib/stores/api.ts

100 lines
3.2 KiB
TypeScript
Raw Normal View History

import type {
MemoryListResponse,
Memory,
SearchResult,
SystemStats,
HealthCheck,
TimelineResponse,
GraphResponse,
DreamResult,
ImportanceScore,
RetentionDistribution,
ConsolidationResult,
feat(dashboard): expose suppress + unsuppress HTTP endpoints + memories UI button Fixes the biggest UI gap flagged by the v2.0.7 audit: the `suppress` tool has shipped since v2.0.5 with full graph event handlers (MemorySuppressed, MemoryUnsuppressed, Rac1CascadeSwept) and violet implosion animations — but zero trigger from anywhere except the MCP layer. Dashboard users literally could not forget a memory without dropping to raw MCP calls. Backend (Axum): - `POST /api/memories/{id}/suppress` optionally accepts `{"reason": "..."}` and returns the full response payload (suppression_count, prior_count, retrieval_penalty, reversible_until, estimated_cascade_neighbors, labile_window_hours). Emits `MemorySuppressed` so the 3D graph plays the compounding violet implosion per the v2.0.6 event handlers. - `POST /api/memories/{id}/unsuppress` reverses within the 24h labile window. Returns `stillSuppressed: bool` so the UI can distinguish a full reversal from a compounded-down state. Emits `MemoryUnsuppressed` for the rainbow burst reversal animation. Frontend: - `api.memories.suppress(id, reason?)` and `api.memories.unsuppress(id)` wired through `apps/dashboard/src/lib/stores/api.ts`. - Two new TypeScript response types (`SuppressResult`, `UnsuppressResult`) in `lib/types/index.ts` mirroring the backend JSON shapes. - Memories page gets a third action button alongside Promote / Demote / Delete: violet "Suppress" with a hover-tooltip explaining "Top-down inhibition (Anderson 2025). Compounds. Reversible for 24h." The button ships `reason: "dashboard trigger"` so the audit log carries source attribution. Tests: 425 mcp + 366 core all pass, svelte-check 580 files 0 errors.
2026-04-19 20:31:44 -05:00
IntentionItem,
SuppressResult,
UnsuppressResult
} 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' }),
feat(dashboard): expose suppress + unsuppress HTTP endpoints + memories UI button Fixes the biggest UI gap flagged by the v2.0.7 audit: the `suppress` tool has shipped since v2.0.5 with full graph event handlers (MemorySuppressed, MemoryUnsuppressed, Rac1CascadeSwept) and violet implosion animations — but zero trigger from anywhere except the MCP layer. Dashboard users literally could not forget a memory without dropping to raw MCP calls. Backend (Axum): - `POST /api/memories/{id}/suppress` optionally accepts `{"reason": "..."}` and returns the full response payload (suppression_count, prior_count, retrieval_penalty, reversible_until, estimated_cascade_neighbors, labile_window_hours). Emits `MemorySuppressed` so the 3D graph plays the compounding violet implosion per the v2.0.6 event handlers. - `POST /api/memories/{id}/unsuppress` reverses within the 24h labile window. Returns `stillSuppressed: bool` so the UI can distinguish a full reversal from a compounded-down state. Emits `MemoryUnsuppressed` for the rainbow burst reversal animation. Frontend: - `api.memories.suppress(id, reason?)` and `api.memories.unsuppress(id)` wired through `apps/dashboard/src/lib/stores/api.ts`. - Two new TypeScript response types (`SuppressResult`, `UnsuppressResult`) in `lib/types/index.ts` mirroring the backend JSON shapes. - Memories page gets a third action button alongside Promote / Demote / Delete: violet "Suppress" with a hover-tooltip explaining "Top-down inhibition (Anderson 2025). Compounds. Reversible for 24h." The button ships `reason: "dashboard trigger"` so the audit log carries source attribution. Tests: 425 mcp + 366 core all pass, svelte-check 580 files 0 errors.
2026-04-19 20:31:44 -05:00
demote: (id: string) => fetcher<Memory>(`/memories/${id}/demote`, { method: 'POST' }),
// v2.0.7: suppress + unsuppress. Anderson 2025 top-down inhibitory
// control. Each suppress call compounds; reversible within 24h. The
// backend emits MemorySuppressed / MemoryUnsuppressed so the 3D graph
// plays the violet implosion / rainbow reversal.
suppress: (id: string, reason?: string) =>
fetcher<SuppressResult>(`/memories/${id}/suppress`, {
method: 'POST',
body: reason ? JSON.stringify({ reason }) : undefined
}),
unsuppress: (id: string) =>
fetcher<UnsuppressResult>(`/memories/${id}/unsuppress`, { 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}`)
};