"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reinforceRoute = reinforceRoute;
exports.weakenRoute = weakenRoute;
exports.decayUnusedEdges = decayUnusedEdges;
exports.pruneDeadEdges = pruneDeadEdges;
exports.computeReward = computeReward;
const DEFAULT_CONFIG = {
    alpha: 0.08,
    beta: 0.02,
    gamma: 0.01,
    pruneThreshold: 0.03,
    maxIdleCycles: 100,
    protectedFloor: 0.25,
};
function reinforceRoute(graph, route, reward, config = {}, episode) {
    const { alpha, gamma } = { ...DEFAULT_CONFIG, ...config };
    for (let i = 0; i < route.length - 1; i++) {
        const edge = graph.getEdge(route[i], route[i + 1]);
        if (!edge)
            continue;
        const target = graph.getNode(edge.target);
        const cost = target?.cost ?? 0;
        // w(t+1) = w(t) + α · reward - γ · cost
        edge.weight += alpha * reward - gamma * cost;
        edge.weight = clamp(edge.weight);
        edge.totalReward += reward;
        edge.lastUsed = new Date().toISOString();
        if (reward > 0)
            edge.successCount++;
        else
            edge.failureCount++;
        // Increase trust on the target node
        if (target && reward > 0) {
            target.trust = clamp(target.trust + alpha * reward * 0.5);
        }
    }
    graph.recordEpisode(episode?.query ?? '', route, reward, {
        taskType: episode?.taskType,
        selectionReasons: episode?.selectionReasons,
        rewardComponents: episode?.rewardComponents,
        dryRun: episode?.dryRun,
        blocked: episode?.blocked,
        blockReason: episode?.blockReason,
        appliedVerifiers: episode?.appliedVerifiers,
    });
}
// ─── Weaken a failed route ──────────────────────────────────────────
function weakenRoute(graph, route, penalty, config = {}) {
    const { alpha, protectedFloor } = { ...DEFAULT_CONFIG, ...config };
    for (let i = 0; i < route.length - 1; i++) {
        const edge = graph.getEdge(route[i], route[i + 1]);
        if (!edge)
            continue;
        // Protected safety/verifier edges that correctly blocked or required
        // verification must not be punished for a failed downstream outcome.
        if (edge.protected) {
            edge.lastUsed = new Date().toISOString();
            // Floor protects them from sliding below the configured minimum.
            if (edge.weight < protectedFloor)
                edge.weight = protectedFloor;
            continue;
        }
        edge.weight -= alpha * penalty;
        edge.weight = clamp(edge.weight);
        edge.failureCount++;
        edge.lastUsed = new Date().toISOString();
        const target = graph.getNode(edge.target);
        if (target && !target.protected) {
            target.trust = clamp(target.trust - alpha * penalty * 0.3);
        }
    }
}
// ─── Decay unused edges ─────────────────────────────────────────────
function decayUnusedEdges(graph, config = {}) {
    const { beta, protectedFloor } = { ...DEFAULT_CONFIG, ...config };
    let decayed = 0;
    for (const edge of graph.listEdges()) {
        edge.weight *= (1 - beta);
        edge.weight = clamp(edge.weight);
        // Protected edges never fall below the floor, even after many cycles.
        if (edge.protected && edge.weight < protectedFloor) {
            edge.weight = protectedFloor;
        }
        decayed++;
    }
    return decayed;
}
// ─── Archive ("prune") weak edges ───────────────────────────────────
//
// Soft-delete edges whose weight has decayed below the configured
// threshold. Protected edges are never archived. Returns the number
// of edges archived.
function pruneDeadEdges(graph, config = {}) {
    const { pruneThreshold } = { ...DEFAULT_CONFIG, ...config };
    let archived = 0;
    // Snapshot via listEdges() because archiveEdge mutates the underlying array.
    for (const edge of graph.listEdges()) {
        if (edge.protected)
            continue;
        if (edge.weight >= pruneThreshold)
            continue;
        if (graph.archiveEdge(edge.source, edge.target, `weight ${edge.weight.toFixed(3)} below threshold ${pruneThreshold}`)) {
            archived++;
        }
    }
    return archived;
}
function computeReward(input) {
    const { taskSuccess = 0, correctness = 0, usefulness = 0, costEfficiency = 0.5, userSatisfaction = 0.5, novelty = 0, } = input;
    return (0.35 * taskSuccess
        + 0.25 * correctness
        + 0.15 * usefulness
        + 0.10 * costEfficiency
        + 0.10 * userSatisfaction
        + 0.05 * novelty);
}
function clamp(value, min = 0, max = 1) {
    return Math.max(min, Math.min(max, value));
}
//# sourceMappingURL=reinforcement.js.map