import { Logger } from '@nestjs/common';
import { type Entry } from '../entry/entry.js';
import type { StorageProvider } from '../storage/storage-provider.js';
import type { ResolvedAlerts } from './alert-rule.js';
export interface TelescopeAlerterDeps {
    alerts: ResolvedAlerts;
    storage: StorageProvider;
    instanceId: string;
    /** Cumulative Recorder drop count reader (delta-based `dropped-entries` rule). */
    droppedCount: () => number;
    /** Wall-clock seam (ms). Defaults to `Date.now`. */
    now?: () => number;
    /** Cap on tracked error families for the `new-exception` rule (test seam). */
    maxFamilies?: number;
    logger?: Logger;
    /**
     * Optional AI-diagnosis lookup for `new-exception` alerts. When set (auto-mode
     * AI configured), the alerter briefly awaits it for the firing family and, if a
     * diagnosis is ready within the grace cap, attaches it to the payload as
     * `diagnosis`. Returning `null` (or being unset) simply means no AI note — the
     * alert fires either way. Never blocks the alert beyond the hook's own cap.
     */
    diagnosisFor?: (familyHash: string) => Promise<string | null>;
}
/**
 * Pluggable-channel alerting (v2). Two evaluation paths:
 *  1. Interval rules (`exception-rate` / `slow-request-rate` / `dropped-entries`)
 *     run on an unref'd timer over windowed counts.
 *  2. The `new-exception` rule runs per-flush via {@link evaluateFlush} over the
 *     just-stored exception entries, so a brand-new error family pages within a
 *     flush interval rather than waiting a full evaluation interval.
 *
 * Every fired alert fans out to ALL configured channels concurrently; one channel
 * failing never blocks the others. NEVER throws into the host: a channel failure
 * is warn-logged (rate-limited per channel) and otherwise swallowed.
 */
export declare class TelescopeAlerter {
    private readonly deps;
    private readonly logger;
    private readonly now;
    private timer;
    /** Per-rule last-fired wall time (index-keyed; rules are a fixed array). */
    private readonly lastFiredAt;
    /**
     * Per-family last-fired wall time for the flush-driven exception rules. Keyed
     * by `${ruleType}|${familyHash}` so a `new-exception` and an `every-exception`
     * rule configured together keep independent cooldown clocks.
     */
    private readonly lastFiredFamily;
    /** Channels we've already warned about (rate-limit failure logs by name). */
    private readonly warnedChannels;
    /** Previous cumulative droppedCount, for the `dropped-entries` delta. */
    private lastDroppedCount;
    /** Bounded per-replica seen-family map backing the `new-exception` rule. */
    private readonly newExceptionTracker;
    /** Pre-computed `new-exception` rule (if any) so the flush path is cheap. */
    private readonly newExceptionRule;
    /** Pre-computed `every-exception` rule (if any) so the flush path is cheap. */
    private readonly everyExceptionRule;
    constructor(deps: TelescopeAlerterDeps);
    /** Start the unref'd evaluation interval. Idempotent. */
    start(): void;
    /** Stop the interval (shutdown). Idempotent. */
    stop(): void;
    /**
     * Evaluate every INTERVAL rule once (the `new-exception` rule is handled by
     * {@link evaluateFlush}, not here). Each firing rule fans out independently.
     * Exposed for the timer and for deterministic tests.
     */
    evaluate(): Promise<void>;
    /**
     * Per-flush evaluation for the exception rules (`new-exception` /
     * `every-exception`). Called with the entries a flush just stored. MUST be
     * cheap: it filters to exception-type entries and does a single bounded-map
     * lookup per family; the expensive batch-context fetch happens ONLY for a
     * family that actually fires (and is past cooldown). No-op (zero cost beyond the
     * early return) when neither rule is configured. Never throws into the host —
     * failures are swallowed/logged.
     */
    evaluateFlush(storedEntries: Entry[]): Promise<void>;
    /**
     * "Is this a NEW error family for the window?" — using the SHARED store dedup
     * when the provider implements `markFamilySeen` (so a family pages once across a
     * multi-replica deployment), and the in-memory per-replica tracker otherwise.
     * A shared-store failure falls back to the local tracker rather than dropping
     * the alert, so a transient store hiccup degrades to once-per-pod, never to
     * silent.
     */
    private observeFamily;
    /** Returns `{ value, threshold }` when an interval rule is firing, else `null`. */
    private measure;
    /** Count entries of `type` recorded in the trailing `window`. */
    private countInWindow;
    /** Count request entries in the window whose `durationMs >= thresholdMs`. */
    private countSlowRequests;
    /**
     * Compute a `metric-threshold` rule's metric over its window and return
     * `{ value, threshold }` when it crosses in the comparator direction, else
     * `null`. Latency metrics read `durationMs` only (content-less); the cache
     * hit-rate needs the cache content, so it reads WITH content for that metric.
     */
    private measureMetric;
    /** Aggregate a metric over the window, or `null` when there are no samples. */
    private computeMetric;
    /**
     * Small windowed read reusing the analytics scan: `omitContent` (alerting only
     * needs counts + `durationMs`, never payloads) and a tight `scanCap`.
     */
    private readWindow;
    private inCooldown;
    /** Composite cooldown key so each flush rule keeps an independent per-family clock. */
    private cooldownKey;
    private familyInCooldown;
    /** Build the v1-compatible payload for an interval (rate) rule. */
    private buildRatePayload;
    /**
     * Build the rich exception payload shared by `new-exception` and
     * `every-exception`. Pulls the exception's own fields, then fetches its batch to
     * find the sibling REQUEST entry for route/method/status/duration/user context,
     * counts how many times this family appears in the trailing window, and (when a
     * `geoLookup` hook is configured) resolves the client IP to a coarse location.
     * This is the ONLY expensive path and runs only on a real fire.
     */
    private buildExceptionPayload;
    /**
     * Resolve a client IP to a coarse {@link AlertGeoLocation} via the host's
     * `geoLookup` hook. Returns `null` when no hook is configured, no IP is present,
     * the hook returns `null`, or the hook throws (swallowed — geo is purely
     * additive and must never break or block an alert beyond the hook's own cost).
     */
    private resolveGeo;
    /**
     * Build the server-exception context: the exception's own fields plus its
     * sibling REQUEST entry (route/method/status/duration/user) from the same batch.
     */
    private buildServerContext;
    /**
     * Build the client-exception context from the browser-reported content. There's
     * no sibling request (the error came straight from the browser), so `route` is
     * the page `url`, `method`/`statusCode`/`durationMs` are null, and `userAgent`
     * carries the reporting browser. The user comes from the entry's own
     * `user:<id>` tag (the controller tagged it at record time).
     */
    private buildClientContext;
    /** Find the sibling REQUEST entry in the exception's batch (or `null`). */
    private findSiblingRequest;
    /** Count entries of this family AND type in the trailing window (>= 1). */
    private countFamilyInWindow;
    /**
     * Fan the payload out to every channel concurrently. Each channel's failure is
     * isolated (`Promise.allSettled`) and warn-logged ONCE per channel name so a
     * persistently-down destination doesn't flood the logs. Never rejects.
     */
    private dispatch;
    /** Warn once per channel name; subsequent failures for that channel are silent. */
    private warnChannelFailure;
}
//# sourceMappingURL=telescope-alerter.d.ts.map