"use strict";
/**
 * RagConfidenceThresholdEnforcer -- Confidence threshold enforcement before
 * RAG-grounded decision gates.
 *
 * DESIGN CONCEPT -- "Decision Gate" vs. "Action Gate"
 * ---------------------------------------------------
 * A *decision gate* fires when the agent commits to a state-change based on
 * retrieved context: "chunk X says drug Y has interaction Z, therefore I will
 * write a prescription note." The commit is the decision -- the gate evaluates
 * whether the retrieval grounding is strong enough to warrant that commit.
 *
 * This is distinct from the autonomy-spectrum gate (src/governance/autonomy-
 * spectrum.ts, PR #24) which fires at the *action* point -- after the decision
 * has already been made but before side-effects are executed.  Both may fire
 * for the same agent event.  Integration recipe:
 *   1. Retrieval adapter records chunks + confidence scores into the cycle's
 *      retrieval pool (via recordRetrieval, which is the PR #25 audit hook).
 *   2. Before committing a state-change decision the orchestrator calls
 *      `RagConfidenceThresholdEnforcer.evaluateDecision(...)`.
 *   3. If the result is 'approval-required', the caller submits to the
 *      ApprovalQueue and halts execution until resolved.
 *   4. If the result is 'denied', the caller aborts without queuing.
 *   5. The autonomy-spectrum gate then fires at the action point independently.
 *
 * THREE-BAND RULE
 * ---------------
 * confidence >= threshold                         -> execute     (NOTICE)
 * threshold * 0.7 <= confidence < threshold       -> approval-required (WARNING)
 * confidence < threshold * 0.7  OR empty chunks  -> denied       (ERROR)
 *
 * WEAKEST-LINK SEMANTICS
 * ----------------------
 * The effective decision confidence is the MINIMUM confidence across all
 * cited chunks.  A chain is only as strong as its weakest link.  An
 * adversary who inflates one chunk's confidence score cannot rescue a
 * decision grounded on a weak chunk.
 *
 * PACK-DEFINED DEFAULTS (ragConfidenceThreshold field on CompliancePack)
 * -----------------------------------------------------------------------
 * HIPAA   0.85  -- clinical context; high-certainty bar for PHI decisions
 * GDPR    0.80  -- significant decisions on personal data require good grounding
 * PCI DSS 0.85  -- financial audit trail demands strong retrieval confidence
 * HMDA    0.80  -- fair-lending decisions must be well-grounded
 * DORA    0.85  -- operational resilience; ICT incidents need accurate context
 * (all other packs) 0.70 non-strict default or operator may leave unset to
 *   opt out of enforcement entirely.
 *
 * INTEGRATION WITH PR #20 (chain-of-custody)
 * -------------------------------------------
 * chunks not present in the cycle's retrieval pool are handled by
 * RagCitationEnforcer (PR #20).  This enforcer defers pool-membership
 * checking to that module -- see `PT-RAG-CONF-3` test.  The dependency
 * is explicit: callers should run RagCitationEnforcer BEFORE
 * RagConfidenceThresholdEnforcer if both are active.
 *
 * AUDIT EVENTS
 * ------------
 * Every `evaluateDecision` call emits exactly one structured audit event.
 * Payloads carry chunk hashes (SHA-256 of chunkRef), never raw text.
 *
 * SECURITY NOTES
 * --------------
 * PT-RAG-CONF-1: confidence inflation -- the gate trusts pool data written
 *   by allowlisted code paths.  If an adversary can write to the pool they
 *   already have broader access.  Compensating control: pool writes are
 *   restricted to recordRetrieval() call sites.
 * PT-RAG-CONF-2: threshold-at-runtime tampering -- configureThreshold()
 *   requires governance.configure permission (OperatorRoles.checkPermission).
 *   Agent-runtime identities (not registered as ADMIN operators) are rejected.
 * PT-RAG-CONF-3: fabricated chunkRefs -- unknown refs produce a denial via
 *   the PR #20 pool-membership guard (RagCitationEnforcer integration).
 *
 * Compliance bindings:
 *   HIPAA 164.308(a)(1)(ii)(D): information system activity review
 *   GDPR Art. 22: automated decision-making safeguards
 *   PCI DSS Req 10.2: log all access to system components
 *   HMDA 12 CFR 1003: fair-lending decision auditability
 *   DORA Art. 9: ICT risk management -- accuracy of automated decisions
 *
 * BUILD_PLAN reference: line 250 -- "Confidence threshold enforcement before
 * decision gates".
 *
 * @connexum/ai-governance
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RAG_CONFIDENCE_NON_STRICT_DEFAULT = exports.RAG_CONFIDENCE_PACK_THRESHOLDS = exports.RagConfidenceThresholdEnforcer = exports.RagConfidenceThresholdPermissionError = exports.RagConfidenceUnboundError = exports.InvalidThresholdError = void 0;
const crypto = __importStar(require("node:crypto"));
// ---------------------------------------------------------------------------
// Public errors
// ---------------------------------------------------------------------------
class InvalidThresholdError extends Error {
    constructor(value) {
        super(`Invalid RAG confidence threshold: ${value}. ` +
            `Threshold must be a finite number in the range [0, 1].`);
        this.name = 'InvalidThresholdError';
    }
}
exports.InvalidThresholdError = InvalidThresholdError;
class RagConfidenceUnboundError extends Error {
    constructor() {
        super('RAG confidence threshold enforcement requires a non-empty packBinding. ' +
            'Per pack-binding-primacy policy, every governed record must name at least ' +
            'one compliance pack.');
        this.name = 'RagConfidenceUnboundError';
    }
}
exports.RagConfidenceUnboundError = RagConfidenceUnboundError;
class RagConfidenceThresholdPermissionError extends Error {
    constructor(identityId, reason) {
        super(`Identity '${identityId}' is not permitted to configure RAG confidence thresholds. ` +
            `Reason: ${reason}. ` +
            `Threshold configuration requires the 'governance.configure' permission (ADMIN role).`);
        this.name = 'RagConfidenceThresholdPermissionError';
    }
}
exports.RagConfidenceThresholdPermissionError = RagConfidenceThresholdPermissionError;
// ---------------------------------------------------------------------------
// Pack-defined default thresholds
// ---------------------------------------------------------------------------
const STRICT_PACK_THRESHOLDS = {
    hipaa: 0.85,
    gdpr: 0.80,
    pcidss: 0.85,
    hmda: 0.80,
    dora: 0.85,
};
const NON_STRICT_DEFAULT = 0.70;
class RagConfidenceThresholdEnforcer {
    config;
    /** Per-(orgId, agentId) operator threshold overrides. */
    overrides = new Map();
    /** SHA-256 of the last emitted audit event per (orgId, agentId), for chain. */
    lastEventSha = new Map();
    constructor(config = {}) {
        this.config = config;
    }
    // -------------------------------------------------------------------------
    // Public API
    // -------------------------------------------------------------------------
    /**
     * Evaluate whether the agent may execute a RAG-grounded decision.
     *
     * @param orgId       Organisation / tenant identifier.
     * @param agentId     Agent identifier.
     * @param cycleId     Current execution cycle identifier.
     * @param decision    Decision descriptor: kind string + cited chunk refs.
     * @param packBinding Active compliance pack IDs.  Must be non-empty.
     * @returns DecisionGateResult with verdict, effective confidence, threshold,
     *          and human-readable reason.
     */
    evaluateDecision(orgId, agentId, cycleId, decision, packBinding) {
        if (!packBinding || packBinding.length === 0) {
            throw new RagConfidenceUnboundError();
        }
        const threshold = this._effectiveThreshold(orgId, agentId, packBinding);
        const { kind, basedOnChunks } = decision;
        // Empty chunks -- agent claims RAG grounding but cites nothing.
        if (!basedOnChunks || basedOnChunks.length === 0) {
            const result = {
                decision: 'denied',
                confidence: 0,
                threshold,
                reason: 'Decision claims RAG grounding but basedOnChunks is empty. ' +
                    'A grounded decision must cite at least one retrieval chunk.',
            };
            this._emitAuditEvent({
                type: 'RAG_CONFIDENCE_TOO_LOW_FOR_APPROVAL',
                severity: 'ERROR',
                orgId,
                agentId,
                cycleId,
                decisionKind: kind,
                chunksConsidered: [],
                confidence: 0,
                threshold,
                decision: 'denied',
                reason: result.reason,
                packBinding,
            });
            return result;
        }
        // Resolve pool entries and compute weakest-link confidence.
        const chunkHashes = [];
        let minConfidence = 1.0;
        let missingChunks = 0;
        for (const chunkRef of basedOnChunks) {
            const entry = this.config.resolvePoolEntry?.(orgId, cycleId, chunkRef) ?? null;
            if (entry === null) {
                // Chunk not in pool -- defer to PR #20 for pool-membership enforcement.
                // From a confidence standpoint, treat as confidence 0 (unknowable).
                missingChunks++;
                chunkHashes.push(this._hashRef(chunkRef));
                minConfidence = 0;
                continue;
            }
            chunkHashes.push(entry.chunkHash);
            if (entry.confidence < minConfidence) {
                minConfidence = entry.confidence;
            }
        }
        if (missingChunks > 0) {
            // At least one chunk is not in the pool -- deny and document dependency
            // on PR #20 for full pool-membership enforcement.
            const result = {
                decision: 'denied',
                confidence: 0,
                threshold,
                reason: `${missingChunks} of ${basedOnChunks.length} cited chunk(s) were not found in the ` +
                    `cycle retrieval pool. Pool-membership enforcement is handled by ` +
                    `RagCitationEnforcer (PR #20). Denying as fail-closed.`,
            };
            this._emitAuditEvent({
                type: 'RAG_CONFIDENCE_TOO_LOW_FOR_APPROVAL',
                severity: 'ERROR',
                orgId,
                agentId,
                cycleId,
                decisionKind: kind,
                chunksConsidered: chunkHashes,
                confidence: 0,
                threshold,
                decision: 'denied',
                reason: result.reason,
                packBinding,
            });
            return result;
        }
        // Apply three-band rule.
        return this._applyBandRule({
            orgId,
            agentId,
            cycleId,
            kind,
            chunkHashes,
            confidence: minConfidence,
            threshold,
            packBinding,
        });
    }
    /**
     * Configure a per-agent threshold override.
     *
     * Requires governance.configure permission.  Agent-runtime identities
     * (not registered as ADMIN operators) are rejected.
     *
     * @param orgId            Organisation / tenant identifier.
     * @param agentId          Agent identifier.
     * @param threshold        New threshold value in [0, 1].
     * @param approverIdentity Operator ID of the human authorising the change.
     * @param packBinding      Active pack IDs (mandatory).
     */
    configureThreshold(orgId, agentId, threshold, approverIdentity, packBinding) {
        if (!packBinding || packBinding.length === 0) {
            throw new RagConfidenceUnboundError();
        }
        if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
            throw new InvalidThresholdError(threshold);
        }
        // Verify the approver has governance.configure permission.
        if (this.config.operatorRoles) {
            const check = this.config.operatorRoles.checkPermission(approverIdentity, 'governance.configure');
            if (!check.allowed) {
                throw new RagConfidenceThresholdPermissionError(approverIdentity, check.reason);
            }
        }
        const key = this._agentKey(orgId, agentId);
        this.overrides.set(key, {
            threshold,
            approverIdentity,
            packBinding,
            configuredAt: new Date().toISOString(),
        });
    }
    /**
     * Returns the threshold currently in effect for the given agent.
     *
     * Resolution order:
     *   1. Operator override (configureThreshold)
     *   2. Strictest pack-defined threshold from packBinding
     *   3. Non-strict default (0.70) if no pack defines a threshold
     */
    getEffectiveThreshold(orgId, agentId, packBinding = []) {
        return this._effectiveThreshold(orgId, agentId, packBinding);
    }
    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------
    _agentKey(orgId, agentId) {
        return `${orgId}::${agentId}`;
    }
    _effectiveThreshold(orgId, agentId, packBinding) {
        const key = this._agentKey(orgId, agentId);
        const override = this.overrides.get(key);
        if (override !== undefined) {
            return override.threshold;
        }
        // Strictest pack-defined threshold wins.
        let packThreshold;
        for (const packId of packBinding) {
            const t = STRICT_PACK_THRESHOLDS[packId];
            if (t !== undefined) {
                if (packThreshold === undefined || t > packThreshold) {
                    packThreshold = t;
                }
            }
        }
        return packThreshold ?? NON_STRICT_DEFAULT;
    }
    _applyBandRule(opts) {
        const { orgId, agentId, cycleId, kind, chunkHashes, confidence, threshold, packBinding } = opts;
        // Band 1: execute.
        if (confidence >= threshold) {
            const result = {
                decision: 'execute',
                confidence,
                threshold,
                reason: `Confidence ${confidence.toFixed(3)} meets threshold ${threshold.toFixed(3)}.`,
            };
            this._emitAuditEvent({
                type: 'RAG_CONFIDENCE_PASSED',
                severity: 'NOTICE',
                orgId,
                agentId,
                cycleId,
                decisionKind: kind,
                chunksConsidered: chunkHashes,
                confidence,
                threshold,
                decision: 'execute',
                reason: result.reason,
                packBinding,
            });
            return result;
        }
        const approvalFloor = threshold * 0.7;
        // Band 2: approval-required.
        if (confidence >= approvalFloor) {
            let approvalRequestId;
            if (this.config.approvalQueue) {
                const req = this.config.approvalQueue.submit(agentId, `RAG_DECISION:${kind}`, `Low-confidence RAG decision (confidence=${confidence.toFixed(3)}, threshold=${threshold.toFixed(3)})`, 'RAG_LOW_CONFIDENCE');
                approvalRequestId = req.id;
            }
            const result = {
                decision: 'approval-required',
                confidence,
                threshold,
                reason: `Confidence ${confidence.toFixed(3)} is below threshold ${threshold.toFixed(3)} ` +
                    `but above approval floor ${approvalFloor.toFixed(3)}. Routed to approval queue ` +
                    `with tier 'RAG_LOW_CONFIDENCE'.`,
                approvalRequestId,
            };
            this._emitAuditEvent({
                type: 'RAG_CONFIDENCE_LOW',
                severity: 'WARNING',
                orgId,
                agentId,
                cycleId,
                decisionKind: kind,
                chunksConsidered: chunkHashes,
                confidence,
                threshold,
                decision: 'approval-required',
                reason: result.reason,
                packBinding,
            });
            return result;
        }
        // Band 3: denied -- too weak even for approval.
        const result = {
            decision: 'denied',
            confidence,
            threshold,
            reason: `Confidence ${confidence.toFixed(3)} is below approval floor ` +
                `${approvalFloor.toFixed(3)} (70% of threshold ${threshold.toFixed(3)}). ` +
                `Retrieval grounding is too weak to permit even human-reviewed approval.`,
        };
        this._emitAuditEvent({
            type: 'RAG_CONFIDENCE_TOO_LOW_FOR_APPROVAL',
            severity: 'ERROR',
            orgId,
            agentId,
            cycleId,
            decisionKind: kind,
            chunksConsidered: chunkHashes,
            confidence,
            threshold,
            decision: 'denied',
            reason: result.reason,
            packBinding,
        });
        return result;
    }
    _emitAuditEvent(partial) {
        const key = this._agentKey(partial.orgId, partial.agentId);
        const prevSha = this.lastEventSha.get(key) ?? '0'.repeat(64);
        const event = {
            ...partial,
            prevSha,
            timestamp: new Date().toISOString(),
        };
        // Update the chain head.
        this.lastEventSha.set(key, crypto.createHash('sha256').update(JSON.stringify(event)).digest('hex'));
        this.config.onAuditEvent?.(event);
    }
    _hashRef(chunkRef) {
        return 'sha256:' + crypto.createHash('sha256').update(chunkRef).digest('hex').slice(0, 16);
    }
}
exports.RagConfidenceThresholdEnforcer = RagConfidenceThresholdEnforcer;
// ---------------------------------------------------------------------------
// Pack threshold constants (for use by pack files and structural tests)
// ---------------------------------------------------------------------------
exports.RAG_CONFIDENCE_PACK_THRESHOLDS = STRICT_PACK_THRESHOLDS;
exports.RAG_CONFIDENCE_NON_STRICT_DEFAULT = NON_STRICT_DEFAULT;
//# sourceMappingURL=rag-confidence-threshold.js.map