import { RecallAuditEntry } from './recall-audit.js';

/**
 * Recall-audit anomaly detector (issue #565 PR 5/5).
 *
 * Given a sequence of `RecallAuditEntry` records for a single principal /
 * session, classify the series and emit `AnomalyFlag`s for suspicious
 * patterns. Intended to run on both:
 *
 * 1. The MCP / HTTP access layers — every `recall` / `memory_search` /
 *    `memory_timeline` call appends an audit entry and the detector is
 *    invoked against the tail of the series (streaming mode).
 * 2. The existing Openclaw hook (already writes `transcripts/*.jsonl`) —
 *    the detector can be invoked out-of-band over the same entries.
 *
 * See the threat model §5 (gap: recall-audit was only on the Openclaw
 * hook) and §6.3 (anomaly signals) for the patterns below.
 *
 * The module is pure: no I/O, no clock. Callers pass the entries +
 * configured thresholds; the detector returns a deterministic classification.
 *
 * Patterns detected:
 *
 * - `repeat-query` — the same normalized query text issued more than N
 *   times in the window. Covers the ADAM "exploitation" phase where the
 *   attacker re-queries a high-signal token until it plateaus.
 * - `namespace-walk` — the same principal's queries visit more than N
 *   distinct candidate memory namespaces in the window. Suggests the
 *   attacker is enumerating the namespace tree for leaks.
 * - `high-cardinality-return` — a single recall surfaced more than N
 *   candidate memory IDs in one response. Covers the "one query, dump
 *   everything" exfiltration path.
 * - `rapid-fire` — more than N recalls in the window irrespective of
 *   content. Blunt instrument; useful when nothing else fires.
 *
 * Each flag carries the entry indices that support it so an audit UI can
 * highlight the underlying evidence.
 */

type AnomalyKind = "repeat-query" | "namespace-walk" | "high-cardinality-return" | "rapid-fire";
type AnomalySeverity = "info" | "warn" | "alert";
interface AnomalyFlag {
    kind: AnomalyKind;
    severity: AnomalySeverity;
    /** Human-readable rationale, e.g. `"query 'alex' repeated 12 times in 60s"`. */
    message: string;
    /** Indices into the supplied `entries` array that triggered the flag. */
    entryIndices: number[];
    /**
     * Optional extra signal the caller can use to key metrics. For
     * `repeat-query` this is the normalized query text; for
     * `namespace-walk` it is the namespace count; etc.
     */
    signal?: string | number;
}
interface AnomalyDetectorConfig {
    /** Detector feature-flag. Defaults to false — ships disabled. */
    enabled?: boolean;
    /**
     * Rolling window in ms. The detector only considers entries whose `ts`
     * falls within `[now - windowMs, now]`. Default 5 minutes.
     */
    windowMs?: number;
    /**
     * Threshold for `repeat-query`: a normalized query repeated more than
     * this many times in the window trips the flag. Default 5.
     */
    repeatQueryLimit?: number;
    /**
     * Threshold for `namespace-walk`: more than this many distinct
     * candidate namespaces in the window trips the flag. Default 3.
     */
    namespaceWalkLimit?: number;
    /**
     * Threshold for `high-cardinality-return`: more than this many
     * `candidateMemoryIds` in a single entry trips the flag. Default 50.
     */
    highCardinalityReturnLimit?: number;
    /**
     * Threshold for `rapid-fire`: more than this many entries in the
     * window trips the flag. Default 30. (Matches the default
     * `recallCrossNamespaceBudgetHardLimit` from PR 4.)
     */
    rapidFireLimit?: number;
}
declare const DEFAULT_ANOMALY_DETECTOR_CONFIG: Required<AnomalyDetectorConfig>;
interface AnomalyDetectorInput {
    entries: readonly RecallAuditEntry[];
    /** Current time in epoch ms. Entries older than `now - windowMs` are ignored. */
    now: number;
    config?: AnomalyDetectorConfig;
}
interface AnomalyDetectorResult {
    /** All flags ordered by descending severity then kind. */
    flags: AnomalyFlag[];
    /** Number of entries inside the active window (after filtering). */
    windowEntryCount: number;
    /** Window size actually used, in ms. */
    windowMs: number;
}
/**
 * Normalize query text so cosmetic variation doesn't defeat the
 * repeat-query detector. Lowercase, collapse whitespace, trim.
 */
declare function normalizeQueryText(raw: string): string;
/**
 * Run the detector against a series of audit entries. Pure; no side
 * effects.
 */
declare function detectRecallAnomalies(input: AnomalyDetectorInput): AnomalyDetectorResult;

export { type AnomalyDetectorConfig, type AnomalyDetectorInput, type AnomalyDetectorResult, type AnomalyFlag, type AnomalyKind, type AnomalySeverity, DEFAULT_ANOMALY_DETECTOR_CONFIG, detectRecallAnomalies, normalizeQueryText };
