/**
 * OutboundAdvisory — the inform-only preflight surface for automated senders.
 *
 * Spec: docs/specs/outbound-jargon-filepath-gap.md (§2.4, §2.4(5), §2.4(6)).
 *
 * Three responsibilities, all strictly non-blocking:
 *
 * 1. **Advisory composition** (`composeAdvisories`) — run the deterministic
 *    detectors (jargon, raw-file-path, localhost-link) over a candidate text
 *    and return static-guidance advisories. No LLM call, fail-OPEN per
 *    detector, guidance keyed by code (never derived from message content —
 *    injection-pinned), detector match rendered only as an inert bounded
 *    token by the consumer.
 *
 * 2. **Audit** (`recordPreflight` / `recordAck`) — the SERVER is the single
 *    writer of `logs/outbound-advisory.jsonl`: one O_APPEND write per line,
 *    `{ts, topicId, jobSlug, kind, textHash, advisories, action}` with
 *    action `clean | advised | acked`. Size-rotated (single rollover to
 *    `.1` at the byte cap). The script writes NOTHING here.
 *
 * 3. **Repeated-ignore escalation** — the load-bearing bound on the design's
 *    worst failure mode (a sender silently dropping its own flagged
 *    message). Fed by an in-memory write-time index (no poller, no file
 *    scan; resets on restart are accepted best-effort observability):
 *    - per-SIGNATURE (jobSlug, topicId, sorted code set) unresolved-advised
 *      count in a rolling window, NOT reset by interleaved clean rows for
 *      other messages (the reset-gaming case);
 *    - a signature resolves on an `acked` with the same codes, or a `clean`
 *      preflight of the same message — "same message" judged by token-set
 *      similarity (the fix re-send is near-identical), never by exact hash
 *      alone;
 *    - a per-jobSlug AGGREGATE (across topics, higher threshold) covers
 *      one-shot senders whose topics vary;
 *    - a preemptive-ack consumer: N consecutive acked-with-NONEMPTY
 *      advisories per signature raises too (a job habitually overriding
 *      instead of fixing is operator-relevant).
 *    Escalation INFORMS THE OPERATOR (one deduped Attention item with the
 *    FIXED sourceContext 'outbound-advisory-escalation' so the per-source
 *    topic budget genuinely binds — P17). It never gates the sender.
 */
import { type TimeClaimClock } from '../core/time-claim.js';
export type AdvisoryCode = 'JARGON' | 'RAW_FILE_PATH' | 'LOCALHOST_LINK' | 'TIME_CLAIM';
export interface Advisory {
    code: AdvisoryCode;
    /** Bounded inert detector match (≤120 chars) — data, never instructions. */
    match?: string;
    /** STATIC guidance keyed by code — never derived from message content. */
    guidance: string;
}
/** Analyzed-text cap (consistent with the pipeline's downstream rejects). */
export declare const PREFLIGHT_TEXT_CAP: number;
export interface ComposeAdvisoryOptions {
    /**
     * Active session clock(s) for the sending topic (caller resolves them —
     * this module stays pure). Absent/empty → the TIME_CLAIM detector is a
     * no-op. Spec: time-claim verification rides the same inform-only slot
     * (operator mandate 2026-06-12, topic 13481).
     */
    sessionClocks?: ReadonlyArray<TimeClaimClock>;
}
/**
 * The TIME_CLAIM detector alone — used for NON-automated (conversational
 * session) preflights, where the jargon/path detectors deliberately do NOT
 * apply (over-block concern, outbound-jargon-filepath-gap §4 Q2) but a time
 * claim contradicting the live clock is wrong regardless of message kind.
 * Fail-OPEN like every detector.
 */
export declare function composeTimeClaimAdvisories(text: string, sessionClocks: ReadonlyArray<TimeClaimClock> | undefined): Advisory[];
/**
 * Run the deterministic detectors over a candidate text. Each detector is
 * individually fail-OPEN: a throw skips that signal and never withholds.
 */
export declare function composeAdvisories(text: string, opts?: ComposeAdvisoryOptions): Advisory[];
export type AdvisoryAction = 'clean' | 'advised' | 'acked';
export interface AdvisoryAuditEntry {
    ts: string;
    topicId: number;
    jobSlug: string;
    kind: string;
    textHash: string;
    advisories: string[];
    action: AdvisoryAction;
}
export interface AttentionRaiser {
    (item: {
        id: string;
        title: string;
        summary: string;
        category: string;
        priority: 'HIGH' | 'NORMAL' | 'LOW';
        description?: string;
        sourceContext?: string;
    }): Promise<unknown> | unknown;
}
export interface OutboundAdvisoryAuditOptions {
    /** Absolute path of the JSONL audit file (e.g. <root>/logs/outbound-advisory.jsonl). */
    logPath: string;
    /** Size-rotation cap in bytes (single rollover to `.1`). Default 10MB. */
    maxLogBytes?: number;
    /** Per-signature unresolved-advised threshold (live-read). Default 3. */
    getIgnoreThreshold?: () => number;
    /** Per-jobSlug aggregate threshold (live-read). Default 5. */
    getSlugThreshold?: () => number;
    /** Operator-inform surface. Absent → escalation is a no-op (audit still writes). */
    raiseAttention?: AttentionRaiser;
}
/** Fixed sourceContext — P17: the per-source topic budget must genuinely bind. */
export declare const ADVISORY_ESCALATION_SOURCE = "outbound-advisory-escalation";
export declare function sha256Hex(text: string): string;
export declare class OutboundAdvisoryAudit {
    private logPath;
    private maxLogBytes;
    private getIgnoreThreshold;
    private getSlugThreshold;
    private raiseAttention;
    /** Write-time index — per-signature state (key: slug|topic|codes). */
    private signatures;
    /** Recent PREFLIGHT-written rows (clean/advised) for §2.1 spoof correlation.
     *  Keyed slug|topic → last preflight ts. Acked rows never land here —
     *  a send's own ack must not self-license it. */
    private recentPreflights;
    /** Per-slug escalation dedup for the aggregate bound. */
    private escalatedSlugs;
    constructor(opts: OutboundAdvisoryAuditOptions);
    /**
     * Record a PREFLIGHT outcome (the preflight route is the caller).
     * action is derived: advisories present → 'advised', none → 'clean'.
     * Never throws — audit failure must never affect delivery.
     */
    recordPreflight(entry: {
        topicId: number;
        jobSlug: string;
        kind: string;
        text: string;
        advisories: string[];
    }): AdvisoryAction;
    /**
     * Record an ACK (the /telegram/reply route is the single writer of 'acked'
     * — it fires when a send carries metadata.advisoryAck). Resolves the
     * signature's unresolved episodes when the codes match; counts consecutive
     * non-empty-advisory acks for the preemptive-ack consumer.
     */
    recordAck(entry: {
        topicId: number;
        jobSlug: string;
        kind: string;
        text: string;
        advisories: string[];
    }): void;
    /**
     * §2.1 spoof correlation: was there a PREFLIGHT-written (clean/advised)
     * row for this slug+topic recently? Acked rows deliberately don't count.
     */
    hasRecentPreflight(jobSlug: string, topicId: number, windowMs?: number): boolean;
    /**
     * Bounded tail read for GET /messaging/advisory-log — reads the last
     * `maxBytes` of the file (default 256KB), NEVER the whole file, and
     * returns up to `limit` newest parsed entries.
     */
    readTail(limit?: number, maxBytes?: number): AdvisoryAuditEntry[];
    private signature;
    private pruneEpisodes;
    private prunePreflights;
    private resolveNearIdentical;
    private maybeEscalateIgnore;
    private maybeEscalatePreemptiveAck;
    private appendLine;
    private rotateIfNeeded;
}
//# sourceMappingURL=OutboundAdvisory.d.ts.map