// agim event bus — fan-out for cross-cutting state changes that the
// dashboard / future plugins want to observe in real time.
//
// Why a dedicated bus instead of just exposing each module's
// EventEmitter?
//
//   1. Type uniformity. Subscribers want to handle *any* event with one
//      handler shape; otherwise tasks.html ends up wiring six different
//      EventEmitters by hand.
//   2. Cross-module decoupling. audit-log / approval-bus / job-board
//      should not know that a Web SSE consumer exists. They emit on the
//      bus; whoever wants to listen does.
//   3. Backpressure / fan-out. The /events SSE endpoint multiplexes a
//      single bus subscription out to N browser clients. If a single
//      client is slow we don't want to drag the entire process; the bus
//      only does fan-out, per-connection write-buffering is the SSE
//      handler's responsibility.
//
// IMPORTANT: emit is best-effort. Never throw out of an emit() — the
// caller (audit-log.logInvocation, approvalBus.handleApproval, etc.) is
// in a hot path. We swallow listener errors here so a buggy subscriber
// can't break the originating action.
import { EventEmitter } from 'node:events';
class ImhubEventBus extends EventEmitter {
    /** Bounded recent-event log so a freshly-connected SSE client can
     *  catch up on what happened in the last few seconds without needing
     *  a separate REST poll. Cap at 200 entries — enough for the dashboard
     *  to show a meaningful "what just happened" feed without bloating
     *  process memory. */
    recent = [];
    static RECENT_CAP = 200;
    constructor() {
        super();
        // EventEmitter defaults to 10 listeners before warning; we expect one
        // handler per SSE connection plus a few internal subscribers, so 100
        // is a comfortable cap that still flags accidental leaks.
        this.setMaxListeners(100);
    }
    /**
     * Publish a typed event. Listeners are called synchronously inside
     * EventEmitter.emit; errors thrown from a listener are caught here so
     * a bad subscriber can't break audit-log / approval-bus / job-board.
     */
    publish(event) {
        this.recent.push(event);
        while (this.recent.length > ImhubEventBus.RECENT_CAP)
            this.recent.shift();
        try {
            this.emit('event', event);
        }
        catch {
            // EventEmitter normally rethrows — caller is in a hot path so we swallow.
        }
    }
    /** Snapshot of the recent ring buffer. Used by SSE handler on
     *  connection to replay the last N events to a fresh client. */
    getRecent() {
        return this.recent.slice();
    }
}
export const eventBus = new ImhubEventBus();
//# sourceMappingURL=event-bus.js.map