/**
 * SkipApiEmptyQueueEnforcer -- Pre-call gate that prevents LLM calls when
 * the agent's work queue is empty.
 *
 * Problem: polling-style agents may issue an LLM call on every tick even
 * when no work is pending, burning tokens for "nothing to do" responses.
 * This gate asks the operator-supplied queue adapter: "is there work?"
 * If the answer is no, it throws EmptyQueueSkipApiError and the caller
 * skips the LLM call entirely.
 *
 * Operator opt-in:
 *   Default: NOT enabled. Operators must call configure() for each agent.
 *   Agents without a registered adapter pass through guardPreCall() as a
 *   no-op. This is intentional -- some agents legitimately poll for events
 *   that are not queue-shaped (cron triggers, health checks, etc.).
 *
 * Position in the hook chain:
 *   VERY EARLY -- before cycle-token-budget, rate-limit, and RAG gates.
 *   If the queue is empty, skip ALL downstream gates and avoid every
 *   compute-heavy check.
 *
 * Fail-closed on adapter error:
 *   If the adapter throws, guardPreCall() rethrows. It does NOT default to
 *   "skip" (which would silently stop the agent) or "allow" (which would
 *   bypass the gate). Both silent behaviours are dangerous. The caller must
 *   handle the error explicitly.
 *
 * Adapter timeout:
 *   Default 500 ms. Queue-state lookups should be fast. A slow queue adapter
 *   is a sign of operator infrastructure issues that should NOT propagate to
 *   LLM-call latency. Timeout is configurable per-agent.
 *
 * Build Plan Reference: docs/BUILD_PLAN_V3_FULL_LIFECYCLE.md line 274
 * Budget-trio siblings: PR #12 (per-cycle), PR #30 (per-agent-daily)
 *
 * Compliance bindings:
 *   - HIPAA 164.312(b): Audit Controls -- idle-agent calls appear as anomalies
 *   - SOC 2 CC6.1: Resource control -- skip unnecessary inference spend
 *   - EU AI Act Art. 9: Risk management -- bound agent action to intent
 *   - ISO 42001 A.6.1.4: Resource management for AI systems
 *
 * @connexum/ai-governance
 */
/**
 * Operator-supplied interface that surfaces the agent's work-queue state.
 *
 * Operators implement this. We ship the gate. The adapter wraps whatever
 * queue technology the operator uses (Redis, BullMQ, SQS, Postgres job
 * table, etc.).
 *
 * Contract:
 *   peek() must return within the configured adapter timeout (default 500 ms).
 *   If it does not, guardPreCall() rejects with QueueAdapterTimeoutError.
 *   This is intentionally tight -- queue state lookups should be fast.
 *   A slow adapter is an operator infrastructure problem, not something the
 *   governance gate should absorb.
 *
 *   peek() must not throw under normal conditions. If it does throw,
 *   guardPreCall() rethrows -- the gate fails closed.
 */
export interface AgentWorkQueueAdapter {
    /**
     * Return the current queue state.
     *
     * @returns hasWork      true if at least one item is pending.
     * @returns workCount    Optional: number of pending items. For telemetry.
     * @returns oldestPendingMs  Optional: age of oldest item in ms. For SLA checks.
     */
    peek(): Promise<{
        hasWork: boolean;
        workCount?: number;
        oldestPendingMs?: number;
    }>;
}
/**
 * Thrown by guardPreCall() when the adapter reports hasWork: false.
 * The caller should catch this and skip the LLM call entirely.
 * It is NOT an error condition -- it is an expected, recoverable signal.
 */
export declare class EmptyQueueSkipApiError extends Error {
    readonly orgId: string;
    readonly agentId: string;
    readonly packBinding: string[];
    constructor(opts: {
        orgId: string;
        agentId: string;
        packBinding: string[];
    });
}
/**
 * Thrown by guardPreCall() when the adapter's peek() does not resolve within
 * the configured timeout. Fails closed -- the LLM call is rejected until
 * the operator fixes their adapter latency.
 */
export declare class QueueAdapterTimeoutError extends Error {
    readonly orgId: string;
    readonly agentId: string;
    readonly timeoutMs: number;
    readonly packBinding: string[];
    constructor(opts: {
        orgId: string;
        agentId: string;
        timeoutMs: number;
        packBinding: string[];
    });
}
/**
 * Thrown when configure() is called with an empty packBinding.
 * Per pack-binding-primacy: every governance record must carry at least one
 * compliance pack binding.
 */
export declare class EmptyQueuePackBindingError extends Error {
    readonly operation: string;
    constructor(operation: string);
}
/**
 * Thrown when configure() is called by an identity that does not hold the
 * 'governance.configure' permission (PT-SKIP-API-3: opt-in bypass prevention).
 */
export declare class ConfigurePermissionError extends Error {
    readonly operatorId: string;
    readonly reason: string;
    constructor(operatorId: string, reason: string);
}
export type SkipApiEventType = 'API_SKIPPED_EMPTY_QUEUE' | 'QUEUE_ADAPTER_CONFIGURED' | 'QUEUE_PEEK_INCONSISTENT';
export type SkipApiSeverity = 'NOTICE' | 'WARNING' | 'ERROR';
export interface SkipApiAuditEvent {
    type: SkipApiEventType;
    severity: SkipApiSeverity;
    orgId: string;
    agentId: string;
    packBinding: string[];
    timestamp: string;
    prevSha: string;
    /** Additional payload fields. */
    [key: string]: unknown;
}
/** Default adapter timeout in milliseconds. */
export declare const DEFAULT_ADAPTER_TIMEOUT_MS = 500;
/**
 * Ratio threshold above which a QUEUE_PEEK_INCONSISTENT warning event is
 * emitted. If peeksClaimingWork / actualLlmCalls > INCONSISTENCY_THRESHOLD,
 * the adapter is reporting hasWork:true significantly more than LLM calls are
 * actually occurring, which may indicate a compromised adapter always returning
 * hasWork:true to keep the gate open. (PT-SKIP-API-1)
 *
 * Evaluated at every skip event (because a skip is direct evidence the adapter
 * returned hasWork:false at least once, giving us a consistent baseline to
 * compare against).
 *
 * Minimum sample size before firing: 10 peeks to avoid false positives on
 * low-volume agents.
 */
export declare const INCONSISTENCY_THRESHOLD = 5;
export declare const INCONSISTENCY_MIN_SAMPLES = 10;
export declare class SkipApiEmptyQueueEnforcer {
    /** Per-agent registered adapters. Key: `${orgId}::${agentId}` */
    private readonly agents;
    /**
     * Audit event callback. Wire to AuditIntegrity.append() in production.
     */
    private readonly onAudit;
    /**
     * SHA chain state for audit events. Simple in-memory chain for the
     * enforcer's own events. In production wire to the external AuditIntegrity chain.
     */
    private lastSha;
    /**
     * Optional permission check function injected by the operator.
     * Signature mirrors OperatorRoles.checkPermission.
     *
     * If not provided, any caller can configure the gate -- appropriate for
     * test environments. In production, inject OperatorRoles.checkPermission
     * to enforce that only operators hold governance.configure.
     */
    private readonly checkPermission?;
    constructor(opts?: {
        /**
         * Audit callback. Called for every event.
         * Must not throw (errors are swallowed to protect the enforcement path).
         */
        onAudit?: (event: SkipApiAuditEvent) => void;
        /**
         * Permission check. If provided, configure() verifies the callerIdentity
         * holds 'governance.configure'. Rejects agent-runtime identities.
         * (PT-SKIP-API-3)
         */
        checkPermission?: (operatorId: string, action: string) => {
            allowed: boolean;
            reason: string;
        };
    });
    /**
     * Register a queue adapter for an agent.
     *
     * Must be called before guardPreCall() has any effect for this agent.
     * If not called, guardPreCall() is a no-op pass-through (opt-in semantics).
     *
     * PT-SKIP-API-3: configure() checks the callerIdentity holds
     * 'governance.configure' permission when a checkPermission function was
     * injected. Agent-runtime identities (prefixed 'agent:' or 'runtime:') are
     * always rejected, even without an injected checker.
     *
     * @param orgId           Tenant / organization ID.
     * @param agentId         Agent identifier.
     * @param callerIdentity  Identity of the operator performing configuration.
     * @param queueAdapter    Operator-implemented adapter.
     * @param packBinding     Non-empty compliance pack binding. (Pack-binding primacy.)
     * @param adapterTimeoutMs  Timeout for peek() calls. Default 500 ms.
     */
    configure(orgId: string, agentId: string, callerIdentity: string, queueAdapter: AgentWorkQueueAdapter, packBinding: string[], adapterTimeoutMs?: number): void;
    /**
     * Pre-call gate. Call this BEFORE every LLM call.
     *
     * Behaviour:
     *   - Agent NOT configured: no-op, returns { allowed: true }. No event emitted.
     *     (Opt-in semantics. Agents without a registered adapter are unaffected.)
     *   - Adapter reports hasWork: true: returns { allowed: true, workCount }.
     *     Increments the actualLlmCalls counter for consistency tracking.
     *   - Adapter reports hasWork: false: throws EmptyQueueSkipApiError.
     *     Caller catches and skips the LLM call. recordSkippedCall() is called
     *     internally to emit the audit event and update counters.
     *   - Adapter throws: rethrows. Gate fails closed. NOT silently skipped or
     *     silently allowed -- both silent paths are dangerous.
     *   - Adapter does not resolve within adapterTimeoutMs: throws
     *     QueueAdapterTimeoutError. Fails closed. (PT-SKIP-API-2)
     *
     * @param orgId     Tenant / organization ID.
     * @param agentId   Agent identifier.
     */
    guardPreCall(orgId: string, agentId: string): Promise<{
        allowed: true;
        workCount?: number;
    }>;
    /**
     * Emit an audit record for a manually recorded skip.
     *
     * Operators may call this when they skip an LLM call outside the
     * guardPreCall() path (e.g., in a wrapper that catches EmptyQueueSkipApiError
     * and wants to add extra context). guardPreCall() calls this internally.
     *
     * @param orgId       Tenant / organization ID.
     * @param agentId     Agent identifier.
     * @param reason      Human-readable reason string.
     * @param packBinding Non-empty compliance pack binding.
     */
    recordSkippedCall(orgId: string, agentId: string, reason: string, packBinding: string[]): void;
    /**
     * Return per-agent skip telemetry for operator dashboards.
     *
     * @param orgId     Tenant / organization ID.
     * @param agentId   Agent identifier.
     */
    getStats(orgId: string, agentId: string): {
        skippedToday: number;
        skippedThisHour: number;
        lastSkipAt: bigint | null;
        configured: boolean;
    };
    private key;
    private currentHourBucket;
    /**
     * Resolve the skip count for the current hour bucket.
     * Resets the bucket counter if we have moved to a new hour.
     */
    private resolveHourBucket;
    /**
     * Execute peek() with a timeout. (PT-SKIP-API-2)
     * Fails closed: timeout throws QueueAdapterTimeoutError (not "skip" or "allow").
     */
    private peekWithTimeout;
    /**
     * Internal skip recording. Updates counters and emits the audit event.
     * Also runs the PT-SKIP-API-1 consistency check.
     */
    private recordSkippedCallInternal;
    /**
     * PT-SKIP-API-1: detect adapter peek inflation.
     *
     * If the ratio of peeks claiming work to actual LLM calls diverges beyond
     * INCONSISTENCY_THRESHOLD (default 5x), the adapter may be compromised --
     * always reporting hasWork:true to keep the gate open while the agent is
     * actually calling the LLM far less frequently.
     *
     * This is a probabilistic signal, not a hard gate. A QUEUE_PEEK_INCONSISTENT
     * warning is emitted for operator review.
     *
     * Minimum sample size: INCONSISTENCY_MIN_SAMPLES (default 10). Avoids
     * false positives on low-volume or newly started agents.
     */
    private checkPeekConsistency;
    /**
     * Determine if the given identity is an agent-runtime identity.
     * Heuristic: agent-runtime IDs are prefixed with 'agent:' or 'runtime:'.
     * In production, wire to OperatorRoles for a hard check.
     */
    private isAgentRuntimeIdentity;
    /**
     * Emit an audit event with SHA-256 chaining.
     * Swallows errors from the onAudit callback to avoid breaking the enforcement path.
     */
    private emit;
}
//# sourceMappingURL=skip-api-empty-queue.d.ts.map