"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Uso = void 0;
const node_crypto_1 = require("node:crypto");
const schema_1 = require("./config/schema");
const logger_1 = require("./events/logger");
const policy_1 = require("./guardrails/policy");
const engine_1 = require("./healing/engine");
const remediation_1 = require("./healing/remediation");
const router_1 = require("./intent/router");
const hook_1 = require("./mcp/hook");
const runner_1 = require("./execution/runner");
const simulator_1 = require("./execution/simulator");
const preconditions_1 = require("./execution/preconditions");
const evidence_1 = require("./execution/evidence");
const discovery_1 = require("./runtime/discovery");
const toolchain_1 = require("./runtime/toolchain");
const defaultConfig = {
    projectRoot: process.cwd(),
    telemetry: { enabled: true, mode: "local" },
    runtime: { preferWsl: false },
    healing: { enabled: true, maxAttempts: 2, backoffMs: 250 },
};
function toUsoError(category, code, message, raw) {
    return { category, code, message, raw };
}
function delay(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
class Uso {
    config = defaultConfig;
    mcpHook;
    constructor(config, mcpHook) {
        if (config) {
            this.config = this.mergeConfig(config);
        }
        this.mcpHook = mcpHook;
    }
    mergeConfig(config) {
        return {
            ...defaultConfig,
            ...config,
            telemetry: {
                enabled: config.telemetry?.enabled ?? defaultConfig.telemetry.enabled,
                mode: "local",
                logDir: config.telemetry?.logDir ?? defaultConfig.telemetry?.logDir,
            },
            runtime: { ...defaultConfig.runtime, ...config.runtime },
            healing: {
                enabled: config.healing?.enabled ?? defaultConfig.healing.enabled,
                maxAttempts: config.healing?.maxAttempts ?? defaultConfig.healing.maxAttempts,
                backoffMs: config.healing?.backoffMs ?? defaultConfig.healing.backoffMs,
            },
            guardrails: config.guardrails,
        };
    }
    async init(config, options) {
        if (config) {
            this.config = this.mergeConfig(config);
        }
        const parsed = schema_1.usoConfigSchema.safeParse(this.config);
        if (!parsed.success) {
            return {
                timestamp: new Date().toISOString(),
                os: process.platform === "win32"
                    ? "windows"
                    : process.platform === "darwin"
                        ? "darwin"
                        : "linux",
                runtimeRoute: "native",
                checks: [],
                warnings: [],
                errors: [parsed.error.message],
            };
        }
        const runtime = (0, discovery_1.discoverRuntime)(parsed.data);
        const toolchain = (0, toolchain_1.fingerprintToolchain)();
        const checks = toolchain.map((t) => ({
            id: `tool-${t.name}`,
            status: t.available
                ? "pass"
                : options?.strict
                    ? "fail"
                    : "warn",
            message: t.available ? `${t.name} available` : `${t.name} missing`,
            detail: t.version ?? t.raw,
        }));
        // Add remediation hints to failed/warned checks
        const checksWithRemediation = checks.map((c) => (0, remediation_1.addRemediationToCheck)(c, runtime.os));
        if (options?.registerMcpHook) {
            await (0, hook_1.registerMcpHook)(this.mcpHook);
        }
        const warnings = checksWithRemediation
            .filter((c) => c.status === "warn")
            .map((c) => c.message);
        const errors = checksWithRemediation
            .filter((c) => c.status === "fail")
            .map((c) => c.message);
        return {
            timestamp: new Date().toISOString(),
            os: runtime.os,
            runtimeRoute: runtime.route,
            checks: checksWithRemediation,
            warnings,
            errors,
        };
    }
    async executeIntent(request) {
        const logger = (0, logger_1.createEventLogger)();
        const startedAt = Date.now();
        const evidenceCollector = new evidence_1.EvidenceCollector();
        const finalizeResult = (result) => {
            if (!this.config.telemetry?.enabled) {
                return result;
            }
            const durationMs = Date.now() - startedAt;
            const record = evidenceCollector.record(result, result.evidence.events, durationMs);
            try {
                const projectRoot = this.config.telemetry?.logDir ?? this.config.projectRoot;
                const evidencePath = evidenceCollector.persistToDisk(projectRoot, record);
                result.evidence.notes.push(`Evidence saved: ${evidencePath}`);
            }
            catch (err) {
                result.evidence.notes.push(`Evidence save failed: ${err.message}`);
            }
            return result;
        };
        logger.emit("intent.received", { intent: request.intent });
        const parsed = schema_1.intentRequestSchema.safeParse(request);
        if (!parsed.success) {
            const invalid = toUsoError("CONFIG_INVALID", "INVALID_INTENT_REQUEST", parsed.error.message);
            return finalizeResult({
                intentId: request.id ?? (0, node_crypto_1.randomUUID)(),
                status: "failed",
                route: "native",
                plan: {
                    intentKind: "check-environment",
                    route: "native",
                    preconditions: [],
                    tasks: [],
                },
                attempts: [],
                evidence: { events: logger.getEvents(), notes: [], reflections: [] },
                error: invalid,
            });
        }
        const runtime = (0, discovery_1.discoverRuntime)(this.config);
        const intentId = parsed.data.id ?? (0, node_crypto_1.randomUUID)();
        let plan;
        try {
            plan = (0, router_1.routeIntent)(parsed.data, runtime.route);
            logger.emit("intent.routed", {
                kind: plan.intentKind,
                route: plan.route,
            });
        }
        catch (err) {
            const unknownIntent = toUsoError("UNKNOWN", "UNKNOWN_INTENT", err.message);
            return finalizeResult({
                intentId,
                status: "blocked",
                route: runtime.route,
                plan: {
                    intentKind: "check-environment",
                    route: runtime.route,
                    preconditions: [],
                    tasks: [],
                },
                attempts: [],
                evidence: { events: logger.getEvents(), notes: [], reflections: [] },
                error: unknownIntent,
            });
        }
        const policyResult = (0, policy_1.evaluateGuardrails)(plan, this.config.guardrails);
        logger.emit("guardrails.checked", policyResult);
        if (!policyResult.ok) {
            return finalizeResult({
                intentId,
                status: "blocked",
                route: plan.route,
                plan,
                attempts: [],
                evidence: {
                    events: logger.getEvents(),
                    notes: [policyResult.reason ?? "Blocked by guardrails"],
                    reflections: [],
                },
                error: toUsoError("POLICY_BLOCKED", "GUARDRAIL_BLOCK", policyResult.reason ?? "Guardrail blocked intent"),
            });
        }
        // Validate preconditions
        const preconditionResults = await (0, preconditions_1.validatePreconditions)(plan);
        logger.emit("preconditions.checked", {
            total: preconditionResults.length,
            satisfied: preconditionResults.filter((r) => r.satisfied).length,
        });
        if (!(0, preconditions_1.allPreconditionsSatisfied)(preconditionResults)) {
            const unsatisfiedPreconditions = preconditionResults
                .filter((r) => !r.satisfied)
                .map((r) => `${r.name}: ${r.message}${r.remedy ? ` (${r.remedy})` : ""}`)
                .join("; ");
            return finalizeResult({
                intentId,
                status: "blocked",
                route: plan.route,
                plan,
                attempts: [],
                evidence: {
                    events: logger.getEvents(),
                    notes: [
                        "Preconditions not satisfied",
                        ...preconditionResults
                            .filter((r) => !r.satisfied)
                            .map((r) => r.remedy ?? `Fix: ${r.name}`),
                    ],
                    reflections: [],
                },
                error: toUsoError("PRECONDITION_FAILED", "PRECONDITION_CHECK_FAILED", unsatisfiedPreconditions),
            });
        }
        const simulation = (0, simulator_1.runSimulation)(plan, this.config.guardrails);
        logger.emit("simulation.completed", simulation);
        if (!simulation.ok &&
            this.config.guardrails?.requireSimulationPass !== false) {
            return finalizeResult({
                intentId,
                status: "blocked",
                route: plan.route,
                plan,
                attempts: [],
                evidence: {
                    events: logger.getEvents(),
                    notes: [simulation.reason ?? "Simulation failed"],
                    reflections: [],
                },
                error: toUsoError("SIMULATION_FAILED", "SIMULATION_BLOCK", simulation.reason ?? "Simulation failed"),
            });
        }
        const attempts = [];
        const reflections = [];
        const maxAttempts = this.config.healing?.maxAttempts ?? 2;
        const baseBackoffMs = this.config.healing?.backoffMs ?? 250;
        for (const task of plan.tasks) {
            let attempt = 1;
            let done = false;
            while (!done) {
                logger.emit("execution.started", { taskId: task.id, attempt });
                const result = await (0, runner_1.runTask)(task, plan.route, attempt);
                attempts.push(result);
                if (result.status === "success") {
                    logger.emit("task.succeeded", { taskId: task.id, attempt });
                    done = true;
                    continue;
                }
                const healing = (0, engine_1.decideHealing)(result.stderr, result.stdout, attempt, maxAttempts, baseBackoffMs);
                logger.emit("healing.decided", healing);
                const reflection = {
                    timestamp: new Date().toISOString(),
                    taskId: task.id,
                    attempt,
                    level: healing.level,
                    decision: healing.decision,
                    reason: healing.reason ?? "No reason provided",
                    stderrSnippet: result.stderr.slice(0, 300),
                    appliedFix: healing.appliedFix,
                };
                reflections.push(reflection);
                const healingPolicy = (0, policy_1.evaluateHealingGuardrails)(plan, healing, this.config.guardrails);
                if (!healingPolicy.ok) {
                    return finalizeResult({
                        intentId,
                        status: "blocked",
                        route: plan.route,
                        plan,
                        attempts,
                        evidence: {
                            events: logger.getEvents(),
                            notes: [healingPolicy.reason ?? "Healing blocked by guardrails"],
                            reflections,
                        },
                        error: toUsoError("POLICY_BLOCKED", "HEALING_BLOCKED", healingPolicy.reason ?? "Healing blocked by guardrails"),
                    });
                }
                if (!healing.shouldRetry) {
                    result.appliedFix = healing.appliedFix;
                    return finalizeResult({
                        intentId,
                        status: "failed",
                        route: plan.route,
                        plan,
                        attempts,
                        evidence: {
                            events: logger.getEvents(),
                            notes: [healing.reason ?? "Execution failed"],
                            reflections,
                        },
                        error: toUsoError("EXECUTION_FAILED", "TASK_FAILED", healing.reason ?? "Task failed", result.stderr || result.stdout),
                    });
                }
                if (healing.retryAfterMs && healing.retryAfterMs > 0) {
                    await delay(healing.retryAfterMs);
                }
                attempt += 1;
            }
        }
        logger.emit("intent.completed", { status: "success" });
        return finalizeResult({
            intentId,
            status: "success",
            route: plan.route,
            plan,
            attempts,
            evidence: { events: logger.getEvents(), notes: [], reflections: [] },
        });
    }
}
exports.Uso = Uso;
//# sourceMappingURL=core.js.map