/**
 * ReadinessRegistry — kernel-side tracker for capability readiness
 * transitions. Spec: `docs/superpowers/specs/2026-04-19-system-readiness-design.md`.
 *
 * Each process owns one registry. The registry:
 *  - Listens to `system.ready-state` events on the event bus.
 *  - Maintains a `Map<key, ReadinessRecord>` snapshot of the most
 *    recently observed state + generation + derived epoch.
 *  - Exposes `emitReady` / `emitStarting` / `emitDown` helpers for
 *    local producers (which stamp the process-wide `generation`).
 *  - Exposes `awaitReady(capName, scope, {timeoutMs, signal})` and
 *    `onReadyState(capName, scope, handler)` for consumers.
 *
 * Epoch derivation:
 *  - First `ready` observed for a `(capName, scope)` → epoch = 1.
 *  - `ready` received with a generation different from the last
 *    `currentGeneration` → epoch++.
 *  - Same-generation repeated `ready` events → epoch unchanged
 *    (idempotent keepalive; e.g. re-emit on `$node.connected`).
 *
 * Not persisted. Rebuilt from the event stream + optional boot-time
 * snapshot rehydration by the caller.
 */
import type { IEventBus, ReadinessScope, ReadinessState } from '../interfaces/event-bus.js';
import type { IScopedLogger } from '../interfaces/logging.js';
import type { IReadinessRegistry, IReadinessHandler, IAwaitReadyOptions, IReadinessTransition } from '../interfaces/readiness.js';
/**
 * Concrete types live here; `interfaces/readiness.ts` declares the
 * matching narrow interfaces (`IReadinessTransition`,
 * `IReadinessHandler`, `IAwaitReadyOptions`) used by `AddonContext`
 * and other consumer-facing types.
 */
export type ReadinessTransition = IReadinessTransition;
export type ReadinessHandler = IReadinessHandler;
export type AwaitReadyOptions = IAwaitReadyOptions;
export declare class ReadinessTimeoutError extends Error {
    readonly capName: string;
    readonly scope: ReadinessScope;
    readonly waitedMs: number;
    constructor(capName: string, scope: ReadinessScope, waitedMs: number);
}
interface ReadinessRecord {
    capName: string;
    scope: ReadinessScope;
    state: ReadinessState;
    generation: string;
    epoch: number;
    lastChange: number;
    /**
     * Node that emitted the latest transition for this record. Needed for
     * `agent.offline` demux of `device`-scoped caps (scope itself carries
     * no node info). For `node`-scoped caps this matches `scope.nodeId`.
     */
    sourceNodeId: string;
}
/**
 * Build a canonical string key for a `(capName, scope)` pair. Used as
 * the snapshot map key and for log/debug output.
 */
export declare function readinessKey(capName: string, scope: ReadinessScope): string;
declare function scopeKey(scope: ReadinessScope): string;
export interface ReadinessRegistryOptions {
    readonly eventBus: IEventBus;
    readonly sourceNodeId: string;
    /**
     * Per-process generation id — stamped on every emit made via this
     * registry's `emitReady`/`emitStarting`/`emitDown` helpers. Caller
     * owns the value so it can survive across subsystem re-bindings
     * within the same process. Default: a random 12-char id.
     */
    readonly generation?: string;
    readonly logger?: IScopedLogger;
    /**
     * Clock source. Injectable for tests.
     */
    readonly now?: () => number;
}
export declare class ReadinessRegistry implements IReadinessRegistry {
    private readonly bus;
    private readonly sourceNodeId;
    private readonly logger;
    private readonly now;
    private readonly generation;
    private readonly snapshot;
    private readonly subscriptions;
    private readonly unsubscribeBus;
    private readonly unsubscribeAgentOffline;
    constructor(options: ReadinessRegistryOptions);
    /** Release the event-bus subscription. Idempotent. */
    close(): void;
    /** Current snapshot for a `(capName, scope)` pair, or `null` if never seen. */
    get(capName: string, scope: ReadinessScope): ReadinessRecord | null;
    /**
     * Serializable snapshot for cross-process transport. Returns an array
     * of `ReadinessRecord` plain objects — safe for MsgPack / JSON
     * transport. Used by the hub's `$readiness.getSnapshot` Moleculer
     * action; consumers hydrate their local registry from the result.
     */
    getSnapshotForTransport(): readonly ReadinessRecord[];
    /**
     * Hydrate the snapshot from an authoritative source. Entries already
     * present locally are skipped — live deltas (received via the event
     * bus subscription) always take precedence over the snapshot. For
     * each newly hydrated entry, a one-shot transition is dispatched to
     * matching subscriptions so pending `awaitReady` callers unblock
     * without having to wait for a fresh event.
     *
     * Local `epoch` is reset to 1 per entry — consumer-side epoch is
     * derived from observed generation transitions, so this mirrors the
     * value that would have been assigned had the consumer observed the
     * first `ready` event directly.
     */
    hydrate(records: readonly ReadinessRecord[]): void;
    /** Shallow copy of the full snapshot — mainly for diagnostics/tests. */
    getAll(): ReadonlyMap<string, ReadinessRecord>;
    /**
     * Emit a `ready` transition for a locally-owned capability. The
     * payload carries this registry's `generation` so remote registries
     * can derive their own `epoch`.
     */
    emitReady(capName: string, scope: ReadinessScope): void;
    emitStarting(capName: string, scope: ReadinessScope): void;
    emitDown(capName: string, scope: ReadinessScope): void;
    /**
     * One-shot: resolve once the cap is `ready`. Reads the snapshot
     * first — if already ready, resolves synchronously on the next tick.
     *
     * Default behaviour is **wait indefinitely** (timeoutMs = `Infinity`):
     * the orchestrator and other callers never want to attach a camera
     * with empty steps just because a cap took longer than 30s to come
     * up. Pass an explicit finite `timeoutMs` to bound the wait, or an
     * `AbortSignal` to cancel externally. `Infinity` is honoured natively
     * — no `setTimeout` is registered (Node's setTimeout silently clamps
     * values above ~24.8d, so we must skip the timer entirely).
     */
    awaitReady(capName: string, scope: ReadinessScope, opts?: AwaitReadyOptions): Promise<void>;
    /**
     * Observable: every transition (starting/ready/down) dispatches to
     * `handler`. On subscription, if the snapshot has a current state,
     * the handler fires ONCE asynchronously with that state as the
     * initial transition (duration = 0) so late subscribers can pick up
     * current state without racing the next transition.
     *
     * Returns an unsubscribe function.
     */
    onReadyState(capName: string, scope: ReadinessScope, handler: ReadinessHandler): () => void;
    private emitTransition;
    /**
     * Update snapshot + dispatch to subscribers. Idempotent: same
     * `generation + state` replay is a no-op for both snapshot and
     * subscribers.
     *
     * Defensive against malformed payloads (legal reason: a mock bus's
     * `getRecent` may ignore the category filter and replay unrelated
     * events when we hydrate at construction time).
     */
    private ingest;
    /**
     * `agent.offline` demux. When an agent disconnects ungracefully,
     * its BaseAddon's shutdown hook doesn't fire — consumers would
     * otherwise keep waiting on a stale `ready`. Walk the snapshot and
     * synthesize a `down` transition for every node-scoped record bound
     * to the disconnected `nodeId`, skipping records already `down`.
     *
     * Synthesis is LOCAL: we don't re-emit on the bus. Every registry
     * (one per process) receives the same `agent.offline` and reacts
     * independently, which means no duplicated downs and no central
     * supervisor required.
     */
    private synthesizeDownForNode;
}
/**
 * Given a list of `(capName, scope)` pairs that a just-disconnected
 * node owned, emit a `down` transition for each. Callers (kernel
 * supervisor, hub `$node.disconnected` handler) wire this up once they
 * know which caps the node held.
 */
export declare function emitDownForOwnedCaps(registry: ReadinessRegistry, owned: ReadonlyArray<{
    readonly capName: string;
    readonly scope: ReadinessScope;
}>): void;
export { scopeKey };
//# sourceMappingURL=readiness-registry.d.ts.map