Adopt shared SSE channel across UI: previews, sessions, workers (+ artifacts via #34) #35
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lz/agent-nexus#35
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Companion to issue #34 (artifact polling → SSE). When deciding how to design that channel, it's wasteful to design it as a one-off — the same machinery should cover every poll point in the app. A codebase survey turned up the following picture: one root
setInterval(10s)in+page.sveltedrives a tick that fans out toWorkers→Sessions→Previews, plus a separate 5s timer insideArtifactsStore. Every meaningful client-side refresh in the app rides one of those two timers.Polling sites found
nexus/src/routes/+page.svelte:14nexus/src/lib/components/Workers.svelte:39,75GET /api/workers+ per-worker…/health+…/sessionscheckWorkerHealthcallable on Docker eventsnexus/src/lib/components/Sessions.svelte:44,69GET /api/workers/:id/sessions+ per-session…/health(TTL-gated to 8s)reconcileSessionsalready emits the deltas we'd pushnexus/src/lib/components/Previews.svelte:19,28GET /api/workers/:id/sessions/:sid/previewsnotify-previewcallback creates apendingrow; operator approves/revokesnotify-previewfrom inside an agent shell is the canonical use case; up to 10s before the operator sees the new pending rowPreviewProxyalready owns the lifecycle eventsnexus/src/lib/artifacts-store.svelte.ts:33GET /api/workers/:id/sessions/:sid/artifactsnotify-artifactcreates row; pin/unpin via UI(Not migration candidates:
SessionTerminalalready uses a WebSocket;LogsModalis one-shot on open; the per-artifact pop-out explicitly disables list polling;FileExplorerrefetches only after its own mutations.)Strong SSE candidates
notify-previeware an explicit operator-action prompt; latency directly degrades the agent's UX inside a shell.reconcileSessionsalready produces the exact delta-stream (window-appeared, window-gone-for-2-polls); pushing it skips both the client 8s TTL gate and the 10s tick.docker eventsfor container start/stop/die; combined with internal create/remove calls inworkers/service.ts, the source events already exist. Slightly bigger lift than the others because we need a Docker-events listener.Marginal candidates
Workers.load'slistSessionscall) — included for free if sessions go SSE; not worth its own channel.needs-setup → ready) — never auto-refetched; only mutated by explicit user action that already callsinvalidateAll. Don't bother.Leave-as-polling
nexus/src/server.ts:31skills-installer staging sweep — server-internal janitor, 5-minute cadence is fine.checkWorkerHealthinvokingreconcileSessions— server-internal; once the client switches to SSE this becomes the producer, not a consumer.Recommended channel design
One shared endpoint,
GET /api/stream(SSE), with topic envelopes. Rationale:hooks.server.tscookie gate already protects/api/*).GETthe canonical list endpoint on (re)connect to reconcile.?topics=workers,sessions:<wid>,previews:<sid>,artifacts:<sid>) so the client only pays bytes for what it subscribes to.Suggested rollout order
notify-artifact), no Docker-events wiring needed. Builds out the shared/api/streamplumbing (envelope shape, server-side bus, client subscription helper) without coupling to the worker poll cascade. Validates topic-filter design end-to-end.notify-previewHTTP entry point + UI approve/revoke mutations). Lets us deletePreviews.svelte's tick subscription independently.reconcileSessionsalready runs on every poll; tee its delta output onto the bus. Detaching from thetickchain materially improves perceived session-creation latency.docker eventssubscriber feeding container-state changes into the bus. Once this lands,nexus/src/routes/+page.svelte's rootsetInterval(10_000)can be deleted entirely, and thetickprop drops out ofMainSplit/Workers/Sessions/Previews.Why open this instead of bundling into #34
Issue #34's scope is narrow and ship-now-ish — the SSE infrastructure itself is what's worth designing once. This issue is the larger architectural picture so reviewers of #34 can see what the channel is being designed for, not just the immediate use case.