resource-events.md 2.7 KB

Resource Events (Per-Workspace Subscription API)

Overview

The Resources layer exposes a per-workspace in-process subscription API for status-change events. This API allows server-side callers to register one handler and receive a unified stream of resource status changes for a specific workspace.

This is currently an internal TypeScript API (not an HTTP endpoint).

API Surface

Entry point:

  • server.resources.getWorkspaceResources(workspaceId)

Subscription method on WorkspaceResources:

subscribe(handler: (event: ResourceEvent) => void): () => void

Behavior:

  • Registers handler for resource events in one workspace.
  • Returns an unsubscribe function that removes all registrations created by this call.
  • Throws NotFoundError when workspaceId does not exist.

Event Shape

ResourceEvent payload:

type ResourceEvent<T = unknown> = {
	group: string;
	version: string;
	resource: string;
	namespace: string;
	name: string;
	status: T;
};

Fields identify the changed Kubernetes resource (group, version, resource, namespace, name) and carry the latest status snapshot.

Current Wiring

Workspace-level subscribe currently aggregates these resource modules:

  • models
  • tools
  • kbs
  • documents
  • agents

stacks and secrets expose their own subscribe(...) methods but are not included in the workspace-level aggregate subscription yet.

Emission Semantics

Underlying emission is implemented by K8sResource and follows these rules:

  • Events are emitted only for update cycles where a resource already seen before changes status.
  • Initial add/list population does not emit an event.
  • Delete events are currently ignored.
  • Change detection is based on JSON string comparison of status (JSON.stringify(status ?? null)).
  • Handlers are called synchronously in registration order.

Initialization and Lifecycle

  • Subscriptions are attached to informer-backed resources.
  • Informers are lazily initialized on first resource access in each module.
  • The workspace-level subscribe function is lightweight and simply composes per-module unsubscribe callbacks.

Error and Concurrency Model

  • Registering/unregistering handlers is in-memory and synchronous.
  • Event dispatch is best-effort in-process delivery; there is no persistence, replay, cursor, or backpressure protocol.
  • If one handler throws, the exception bubbles from the dispatch loop (handlers should not throw).

Scope and Non-Goals

This API is intentionally scoped to server-internal observers.

Out of scope in current implementation:

  • cross-process fanout
  • durable event log or replay
  • transport protocol (SSE/WebSocket)
  • delivery guarantees (at-least-once/exactly-once)