/**
 * EventDispatcher — the central event bus (one per runner).
 *
 * Pattern: Observer (GoF) + Pub/Sub over a typed discriminated union.
 * Role:    Single flat dispatcher for every event emitted during a run.
 *          Replaces DOM-style bubbling — we have a central bus by
 *          construction, so tree propagation is unnecessary.
 * Emits:   N/A — this IS the emitter. It ROUTES events to listeners.
 *
 * Semantics:
 *   - Observers are ALWAYS fire-and-forget (inherited from footprintjs's
 *     recorder contract). Promise returns are never awaited.
 *   - Listener errors are caught; they become `agentfootprint.error.fatal`
 *     events with stage:'observer'. The run continues.
 *   - Dispatch is O(1) hash lookup by event type.
 *   - Zero allocation when no listener for an event type AND no wildcard.
 *   - Dev-mode wraps listeners to warn on async listener Promise return.
 *   - Lifecycle: subscriptions release via the returned Unsubscribe or an
 *     AbortSignal (`{ signal }`); `removeAllListeners()` is the bulk
 *     escape hatch for long-lived server consumers; `listenerCount()` is
 *     the leak diagnostic. Every removal path prunes emptied buckets and
 *     detaches abort handlers, so listener storage is bounded by LIVE
 *     subscriptions — never by subscription history.
 */
import type { EventServing } from '../redaction/served.js';
import type { AgentfootprintEvent, AgentfootprintEventMap, AgentfootprintEventType } from './registry.js';
export type EventListener<K extends AgentfootprintEventType> = (event: AgentfootprintEventMap[K]) => void;
export type WildcardListener = (event: AgentfootprintEvent) => void;
export interface ListenOptions {
    readonly once?: boolean;
    readonly signal?: AbortSignal;
}
export type Unsubscribe = () => void;
export type DomainWildcard = 'agentfootprint.composition.*' | 'agentfootprint.agent.*' | 'agentfootprint.stream.*' | 'agentfootprint.context.*' | 'agentfootprint.memory.*' | 'agentfootprint.tools.*' | 'agentfootprint.skill.*' | 'agentfootprint.permission.*' | 'agentfootprint.validation.*' | 'agentfootprint.reliability.*' | 'agentfootprint.credential.*' | 'agentfootprint.map.*' | 'agentfootprint.integrity.*' | 'agentfootprint.fallback.*' | 'agentfootprint.cost.*' | 'agentfootprint.eval.*' | 'agentfootprint.error.*' | 'agentfootprint.resilience.*' | 'agentfootprint.pause.*' | 'agentfootprint.checkin.*' | 'agentfootprint.middleware.*' | 'agentfootprint.embedding.*' | 'agentfootprint.artifacts.*' | 'agentfootprint.ontology.*' | 'agentfootprint.answer.*';
export type AllWildcard = '*';
export type WildcardSubscription = DomainWildcard | AllWildcard;
/**
 * Central event bus. One per executable runner.
 *
 * Zero-alloc fast path: if `hasListenersFor(type)` is false AND there are
 * no wildcards, `dispatch` returns immediately without iteration.
 */
export declare class EventDispatcher {
    private readonly byType;
    private readonly domainWildcards;
    private readonly allWildcards;
    /**
     * What the current run's events are served as (`src/redaction/served.ts`),
     * installed by the runner at the start of each run. Absent until a run
     * opens one (then the instance's declaration serves, `instanceServing`) —
     * and a run with no policy installs the identity, so every listener
     * receives the very object that was dispatched, as before.
     */
    private serving;
    /**
     * Each run's serving by its `runId` — so a fact a run dispatches AFTER the
     * next run opened (a late artifact fact, a tool session's teardown report, a
     * host's attributed fact; each stamped with its own run's id) is still served
     * under ITS run's policy, never the one in force now. An event stamped with
     * no run's id (`'consumer-scope'`, a resume's reply) is served under the
     * current one. Bounded to the most recent runs ({@link RUN_SERVINGS_KEPT}).
     */
    private readonly servingsByRun;
    /**
     * The serving for an event of NO run dispatched before any run opened one —
     * a consumer's `emit`, a host's fact — resolved from what the runner
     * DECLARES (`RunnerBase`): its policy, positively none, or `REFUSE` when the
     * declaration is unknown. A dispatcher no runner owns carries no run's
     * records and declares none: it serves as dispatched.
     */
    private instanceServing;
    /**
     * Whether a run id names a RUN (as opposed to an address that names none —
     * a consumer's own emit, an event before the first run), supplied by the
     * runner, which owns the id format (`RunnerBase · isMintedRunId`). A fact
     * stamped with a run this dispatcher does not hold a serving for (past the
     * bound, or never opened here) is served as the placeholder — never under a
     * DIFFERENT run's policy.
     */
    private namesARun;
    /**
     * The REAL-value path — the library's own mechanisms that run on events
     * (the crash checkpoint, the window's token reading, the reply a host
     * streams). Never a record: see `src/redaction/runRedaction.ts`. Separate
     * from every consumer bucket on purpose, so `removeAllListeners()` — a
     * consumer's lifecycle call — cannot take the agent's machinery with it.
     */
    private readonly realListeners;
    /**
     * Fast-path check. Returns true when at least one listener would fire
     * for this type. Used by emitters to skip event-object allocation.
     */
    hasListenersFor(type: AgentfootprintEventType): boolean;
    /**
     * Subscribe a typed listener for a specific event type.
     *
     * The listener signature is `(event) => void` by design — Promises are
     * NOT awaited. See dispatch() for details.
     */
    on<K extends AgentfootprintEventType>(type: K, listener: EventListener<K>, options?: ListenOptions): Unsubscribe;
    /** Subscribe to a domain wildcard ('agentfootprint.context.*') or '*'. */
    on(type: WildcardSubscription, listener: WildcardListener, options?: ListenOptions): Unsubscribe;
    /**
     * Subscribe a one-shot listener. Fires at most once and then auto-removes.
     * Equivalent to `on(type, listener, { once: true })`. Accepts `{ signal }`
     * for AbortSignal auto-cleanup, same as `on()`.
     */
    once<K extends AgentfootprintEventType>(type: K, listener: EventListener<K>, options?: Omit<ListenOptions, 'once'>): Unsubscribe;
    once(type: WildcardSubscription, listener: WildcardListener, options?: Omit<ListenOptions, 'once'>): Unsubscribe;
    /**
     * Shared subscribe path for on()/once(). The public overloads constrain
     * `type` to either typed keys or wildcards; internally the dispatcher's
     * bucket logic accepts any string and classifies by shape.
     */
    private subscribe;
    /**
     * Remove a specific listener for a type. Prefer AbortSignal for auto-cleanup.
     *
     * Because listeners are wrapped in dev mode, identity is preserved via a
     * WeakMap in addListener — consumers pass the original function.
     */
    off<K extends AgentfootprintEventType>(type: K, listener: EventListener<K>): void;
    off(type: WildcardSubscription, listener: WildcardListener): void;
    /**
     * Lifecycle escape hatch — drop EVERY listener (typed, domain-wildcard,
     * and `'*'`) in one call. For long-lived server consumers that reuse one
     * runner across many requests: when you can't thread an AbortSignal or
     * keep every Unsubscribe handle, call this between requests to guarantee
     * the dispatcher holds zero subscriptions.
     *
     * Safe to call mid-dispatch: the bucket currently being iterated
     * finishes its already-taken snapshot (same semantics as `off()` during
     * dispatch), buckets the in-flight dispatch has NOT yet reached deliver
     * nothing (DOM-like "stop now"), and every SUBSEQUENT event sees no
     * listeners. Abort handlers registered on consumer AbortSignals via
     * `{ signal }` are detached too. Previously returned Unsubscribe
     * handles become harmless no-ops.
     */
    removeAllListeners(): void;
    /**
     * Diagnostic — how many listeners the dispatcher currently retains.
     *
     * - `listenerCount()` — TOTAL across every bucket (typed + domain
     *   wildcards + `'*'`). The number long-lived consumers watch to verify
     *   per-run subscriptions are being released (leak detection).
     * - `listenerCount(type)` — listeners registered under that exact
     *   subscription key (`'agentfootprint.agent.turn_start'`,
     *   `'agentfootprint.context.*'`, or `'*'`). NOTE: counts the bucket
     *   only — a typed count does NOT include wildcard listeners that would
     *   also fire for that type. "Would anything fire?" is
     *   `hasListenersFor()`.
     */
    listenerCount(type?: AgentfootprintEventType | WildcardSubscription): number;
    /**
     * Route an event to all matching listeners (typed + domain-wildcard + all).
     *
     * Under a run's redaction policy every listener receives the SERVED event —
     * its payload and the identity on its meta through the run's rule
     * (`src/redaction/served.ts`); with no policy, the very object dispatched.
     *
     * Fire-and-forget: any returned Promise is IGNORED. Listener exceptions are
     * caught (and logged in dev mode); the run continues regardless.
     */
    dispatch(event: AgentfootprintEvent): void;
    /**
     * Install the serving for events of no run dispatched before any run opened
     * one (`instanceServing`) — read at dispatch time, so a declaration made
     * after the dispatcher exists still applies. `REFUSE` when the runner's
     * declaration is unknown.
     *
     * @internal
     */
    useInstanceServing(serving: () => EventServing | typeof REFUSE): void;
    /**
     * Install the recogniser of run ids (`namesARun`): a fact stamped with a run
     * this dispatcher holds no serving for is then refused — served as the
     * placeholder — instead of served under the run in force.
     *
     * @internal
     */
    useRunIdRecogniser(namesARun: (runId: string) => boolean): void;
    /**
     * Install what this run's events are served as. Called by the runner when a
     * run opens (`RunnerBase · openRunRedaction`) and before a resume's reply.
     *
     * @param serving  the run's serving — every run has one: covered by a
     *                 policy, or positively none (the identity, which still
     *                 serves the marks a run makes)
     * @param runId    the run's id, to serve its late facts by
     * @internal
     */
    useServing(serving: EventServing, runId?: string): void;
    /**
     * Dispatch a fact the runner files about ONE run after its executor returned
     * (its pause request) — served under THAT run's serving
     * (`runRedaction.ts` · `servingOfExecutor`), never the run in force now: on
     * an instance whose runs overlap, the last run opened is another run. A fact
     * whose run has no serving (`undefined` — an executor no run of this library
     * opened) is refused: served as the placeholder. The real-value path
     * receives it as made.
     *
     * @internal
     */
    dispatchForRun(event: AgentfootprintEvent, serving: EventServing | undefined): void;
    /**
     * Dispatch an event whose PAYLOAD was already served at its source — the
     * emit helper serves it before `$emit` (`src/redaction/runRedaction.ts` ·
     * `emitServed`), so every footprintjs channel and this one share one served
     * payload. Only the meta's identity is left to serve here. The real-value
     * path already had this event from the source, so it is not fed again.
     *
     * @internal — the EmitBridge's.
     */
    dispatchServed(event: AgentfootprintEvent): void;
    /**
     * Subscribe to the REAL-value path: every event as its producer made it,
     * never served. For the library's own mechanisms only — what the agent
     * computes on or hands back to its caller (see the field note). Listener
     * errors are isolated like any other.
     *
     * @internal
     */
    onRealEvent(listener: (event: AgentfootprintEvent) => void): Unsubscribe;
    /** @internal — lets the emit helper skip building an event nobody reads. */
    hasRealListeners(): boolean;
    /**
     * Deliver one event to the real-value path only.
     *
     * @internal — the emit helper's, and `dispatch`'s.
     */
    deliverReal(event: AgentfootprintEvent): void;
    /**
     * Route one event to every consumer bucket (typed + domain + all), served
     * once — and only when some bucket would receive it, so an event nobody
     * listens to costs no walk.
     */
    private fanOut;
    /** The event as its resolved serving serves it — the same object when nothing was selected. */
    private served;
    /**
     * What `event` is served under, resolved to one of three states — never
     * inferred from an absent value (`redaction/coverage.ts`):
     *
     *   - a fact of a run this dispatcher HOLDS: that run's serving (covered, or
     *     positively none);
     *   - a fact of a run it does not hold — evicted past
     *     {@link RUN_SERVINGS_KEPT}, or never opened here (another instance's,
     *     an earlier process's): UNKNOWN, refused, whatever the instance
     *     declares;
     *   - a fact of NO run (a consumer's emit, a resume's reply): the run in
     *     force, else what the runner declares (`instanceServing`).
     */
    private servingFor;
    private addListener;
    /**
     * Bounded-leak guarantee: a bucket emptied by ANY removal path is
     * deleted from its Map so `byType` / `domainWildcards` never retain
     * empty Sets for event types subscribed once and released. The
     * identity check (`get(...) === bucket`) guards stale Unsubscribe
     * closures — they must never delete a NEWER bucket re-created under
     * the same key after this one was pruned. (`allWildcards` is a stable
     * field, not a Map entry — nothing to prune.)
     */
    private pruneBucket;
    private ensureBucket;
    private bucketFor;
    private fireBucket;
    private domainKey;
}
/** The resolution of an event whose run's state is UNKNOWN: refuse it (`refusedEvent`). */
export declare const REFUSE: unique symbol;
//# sourceMappingURL=dispatcher.d.ts.map