"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sentinel = exports.Sentinel = void 0;
const event_buffer_1 = require("./internal/event-buffer");
const config_1 = require("./config");
const cancel_adapter_1 = require("./adapters/cancel-adapter");
const telemetry_1 = require("./adapters/telemetry");
const audit_1 = require("./adapters/audit");
const policies_1 = require("./policies");
const violation_publisher_1 = require("./violation-publisher");
class Sentinel {
    constructor() {
        this.subscriptions = new Map();
        this.evaluationIntervals = new Map();
        this.eventBuffer = new event_buffer_1.EventBuffer();
        this.config = { ...config_1.sentinelConfig };
        setInterval(() => {
            this.eventBuffer.cleanup();
        }, 5 * 60 * 1000);
    }
    static getInstance() {
        if (!Sentinel.instance) {
            Sentinel.instance = new Sentinel();
        }
        return Sentinel.instance;
    }
    static resetInstance() {
        if (Sentinel.instance) {
            Sentinel.instance.shutdown();
            Sentinel.instance = undefined;
        }
    }
    watchRun(runId, events$) {
        console.log(`🔍 Sentinel: Watching run ${runId}`);
        const subscription = events$.subscribe((event) => {
            this.handleEvent(event);
        });
        this.subscriptions.set(runId, subscription);
        if (this.config.enabled) {
            this.setupEvaluationInterval(runId);
        }
    }
    handleEvent(event) {
        this.eventBuffer.processEvent(event);
        if (this.config.enabled) {
            this.evaluatePolicies(event.runId);
        }
        if (event.type === 'finish' || event.type === 'error') {
            this.cleanupRun(event.runId);
        }
    }
    setupEvaluationInterval(runId) {
        const interval = setInterval(() => {
            if (this.config.enabled) {
                this.evaluatePolicies(runId);
            }
        }, 1000);
        this.evaluationIntervals.set(runId, interval);
    }
    cleanupRun(runId) {
        const interval = this.evaluationIntervals.get(runId);
        if (interval) {
            clearInterval(interval);
            this.evaluationIntervals.delete(runId);
        }
        const subscription = this.subscriptions.get(runId);
        if (subscription) {
            subscription.unsubscribe();
            this.subscriptions.delete(runId);
        }
    }
    syncExternalKill(runId, reason) {
        const runState = this.eventBuffer.getRunState(runId);
        if (runState?.status === 'killed' || runState?.status === 'finished') {
            return;
        }
        if (runState) {
            this.eventBuffer.markKilled(runId, reason);
        }
        this.cleanupRun(runId);
    }
    async kill(runId, reason) {
        console.log(`🛑 Sentinel: Killing run ${runId} - Reason: ${reason}`);
        const runState = this.eventBuffer.getRunState(runId);
        if (runState?.status === 'killed' || runState?.status === 'finished') {
            console.log(`⚠️ Sentinel: Run ${runId} already ${runState.status}`);
            return;
        }
        try {
            this.eventBuffer.markKilled(runId, reason);
            const cancelResult = await cancel_adapter_1.cancelAdapter.cancelRun(runId, reason);
            telemetry_1.telemetryAdapter.logKill(runId, reason, {
                cancelSuccess: cancelResult.success,
                cancelReason: cancelResult.reason,
                timestamp: cancelResult.timestamp
            });
            await audit_1.auditAdapter.logKill(runId, reason, this.config, {
                cancelResult,
                runState: runState ? {
                    status: runState.status,
                    tokenCount: runState.tokenCount,
                    duration: runState.startedAt ? Math.max(0, Date.now() - runState.startedAt) : undefined
                } : undefined
            });
            this.cleanupRun(runId);
            console.log(`✅ Sentinel: Run ${runId} killed successfully`);
        }
        catch (error) {
            console.error(`❌ Sentinel: Failed to kill run ${runId}:`, error);
            telemetry_1.telemetryAdapter.logKill(runId, reason, {
                success: false,
                error: error instanceof Error ? error.message : 'Unknown error'
            });
            this.eventBuffer.markKilled(runId, reason);
            this.cleanupRun(runId);
        }
    }
    getActiveRuns() {
        return this.eventBuffer.getActiveRuns();
    }
    getRunStatus(runId) {
        return this.eventBuffer.getRunState(runId);
    }
    getRecentEvents(runId, count = 10) {
        return this.eventBuffer.getRecentEvents(runId, count);
    }
    getTokenEventsInWindow(runId, windowMs) {
        return this.eventBuffer.getTokenEventsInWindow(runId, windowMs);
    }
    getPolicyViolations(runId) {
        const runState = this.eventBuffer.getRunState(runId);
        if (!runState) {
            return [];
        }
        return policies_1.policyManager.evaluatePolicies(runId, runState, this.config);
    }
    async evaluatePolicies(runId) {
        try {
            const runState = this.eventBuffer.getRunState(runId);
            if (!runState || runState.status !== 'running') {
                return;
            }
            const violations = policies_1.policyManager.evaluatePolicies(runId, runState, this.config);
            if (violations.length > 0) {
                const primaryViolation = violations[0];
                for (const violation of violations) {
                    console.log(`🚨 Sentinel: Policy violation detected for run ${runId}: ${violation.policy} - ${violation.reason}`);
                    telemetry_1.telemetryAdapter.logPolicyViolation(runId, violation.policy, violation.threshold, {
                        reason: violation.reason,
                        actual: violation.actual,
                        timestamp: violation.timestamp
                    });
                    try {
                        await audit_1.auditAdapter.logPolicyViolation(runId, violation.policy, violation.threshold, this.config, {
                            reason: violation.reason,
                            actual: violation.actual,
                            timestamp: violation.timestamp
                        });
                    }
                    catch (error) {
                        console.error(`Failed to log policy violation audit for run ${runId}:`, error);
                    }
                }
                if (primaryViolation) {
                    try {
                        await (0, violation_publisher_1.notifyPolicyViolation)(primaryViolation);
                    }
                    catch (error) {
                        console.error(`Failed to notify policy violation publisher for run ${runId}:`, error);
                    }
                    await this.kill(runId, `policy_violation:${primaryViolation.policy}`);
                }
            }
        }
        catch (error) {
            console.error(`Policy evaluation failed for run ${runId}:`, error);
        }
    }
    unwatchRun(runId) {
        console.log(`👋 Sentinel: Unwatching run ${runId}`);
        this.cleanupRun(runId);
    }
    getWatchedRunIds() {
        return Array.from(this.subscriptions.keys());
    }
    isEnabled() {
        return this.config.enabled;
    }
    getConfig() {
        return { ...this.config };
    }
    updateConfig(newConfig) {
        if (newConfig.runMaxDurationMs <= 0) {
            throw new Error('runMaxDurationMs must be positive');
        }
        if (newConfig.runMaxTokens <= 0) {
            throw new Error('runMaxTokens must be positive');
        }
        if (newConfig.runIdleMs <= 0) {
            throw new Error('runIdleMs must be positive');
        }
        if (newConfig.loopWindow <= 0) {
            throw new Error('loopWindow must be positive');
        }
        if (newConfig.loopMax <= 0) {
            throw new Error('loopMax must be positive');
        }
        if (newConfig.loopMax > newConfig.loopWindow) {
            throw new Error('loopMax cannot exceed loopWindow');
        }
        this.config = { ...newConfig };
        console.log(`✅ Sentinel: Configuration updated`, {
            enabled: this.config.enabled,
            runMaxDurationMs: this.config.runMaxDurationMs,
            runMaxTokens: this.config.runMaxTokens,
            runMaxCost: this.config.runMaxCost
        });
    }
    shutdown() {
        console.log(`🔄 Sentinel: Shutting down`);
        for (const interval of this.evaluationIntervals.values()) {
            clearInterval(interval);
        }
        this.evaluationIntervals.clear();
        for (const subscription of this.subscriptions.values()) {
            subscription.unsubscribe();
        }
        this.subscriptions.clear();
        this.eventBuffer.cleanup();
    }
}
exports.Sentinel = Sentinel;
exports.sentinel = Sentinel.getInstance();
//# sourceMappingURL=sentinel.js.map