Files
oikos/web/src/lib/stores/context.ts
dtoro 0a3654b08f refactor(web): delete dead code (R2) — 1736 lines removed
Tool-renderer registry (21 files, ~1.5k lines):
- src/lib/tool-renderers.ts — registry + getToolRenderer (exported, never
  imported anywhere)
- src/lib/renderers/index.ts + 10 .ts registrars + 10 .svelte components
- main.ts: removed the requestAnimationFrame(() => import('./lib/renderers'))
  that was the only thing keeping the dead subsystem alive

Dead components (never imported):
- ToolCallGroup, PlanProgress, GoalHeader, InlineApproval, SessionDigest

Dead store exports (written, never read):
- context.ts: pendingApprovals writable (+ Approval type import)
- events.ts: connectionState writable (+ its .set() calls)

Dead API surface:
- api.ts: SessionDigest interface + fetchSessionDigest (only caller was the
  dead SessionDigest.svelte)

Dead npm deps:
- mode-watcher (0 imports; superseded by stores/theme.svelte.ts)
- @internationalized/date (0 imports)

Also: fix stale comments referencing deleted symbols, update plan R1/R2
status. Build clean (4683 modules, down from 4706; one Svelte 5 warning
gone — the dead HealthSummary.svelte was emitting state_referenced_locally).
2026-07-17 22:10:27 +02:00

62 lines
1.8 KiB
TypeScript

import { writable, get } from 'svelte/store'
import { fetchDashboardSummary, fetchApprovals, type DashboardSummary } from '$lib/api'
import { liveEvents, subscribeEvents, type OikosEvent } from './events'
// Shared operational context: dashboard summary + pending approvals,
// refreshed on a slow poll and eagerly on relevant SSE events. Ref-counted
// so the poll only runs while something on screen displays it.
export const summary = writable<DashboardSummary | null>(null)
let refs = 0
let pollTimer: ReturnType<typeof setInterval> | null = null
let unsubscribeSSE: (() => void) | null = null
let unsubscribeStore: (() => void) | null = null
let lastSeenEventId = 0
export async function refreshContext() {
const [s] = await Promise.all([fetchDashboardSummary(), fetchApprovals('pending')])
if (s) summary.set(s)
}
function onEvent(ev: OikosEvent) {
if (ev.id <= lastSeenEventId) return
lastSeenEventId = ev.id
if (
ev.type.startsWith('approval.') ||
ev.type.startsWith('signal.') ||
ev.type.startsWith('execution.') ||
ev.type === 'health.changed'
) {
refreshContext()
}
}
export function subscribeContext(): () => void {
refs++
if (refs === 1) {
refreshContext()
pollTimer = setInterval(refreshContext, 30000)
unsubscribeSSE = subscribeEvents()
unsubscribeStore = liveEvents.subscribe((events) => {
if (events[0]) onEvent(events[0])
})
}
return () => {
refs--
if (refs === 0) {
if (pollTimer) clearInterval(pollTimer)
pollTimer = null
unsubscribeSSE?.()
unsubscribeSSE = null
unsubscribeStore?.()
unsubscribeStore = null
}
}
}
export function openSignalCount(s: DashboardSummary | null): number {
if (!s) return 0
return Object.values(s.signals_by_severity).reduce((a, b) => a + b, 0)
}