"use strict";
// Golden traces — replayable reference traces for regression detection.
//
// Captures exemplary agent runs as "golden traces" that serve as
// behavioural baselines. Future runs are compared against them to
// detect drift in output, tool usage, or file modifications.
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.saveGoldenTrace = saveGoldenTrace;
exports.loadGoldenTrace = loadGoldenTrace;
exports.listGoldenTraces = listGoldenTraces;
exports.deleteGoldenTrace = deleteGoldenTrace;
exports.compareWithGolden = compareWithGolden;
exports.captureFromRun = captureFromRun;
exports.renderDriftReport = renderDriftReport;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
// ─── Helpers ─────────────────────────────────────────────────────────
const TRACES_DIR = '.harness/golden-traces';
function tracesDir(projectDir) {
    return path.join(projectDir, TRACES_DIR);
}
function traceFile(projectDir, id) {
    return path.join(tracesDir(projectDir), `${id}.json`);
}
/** Token-level Jaccard similarity: |intersection| / |union|. */
function jaccardSimilarity(a, b) {
    const setA = new Set(a.split(/\s+/).filter(Boolean));
    const setB = new Set(b.split(/\s+/).filter(Boolean));
    if (setA.size === 0 && setB.size === 0)
        return 1.0;
    let intersection = 0;
    for (const token of setA) {
        if (setB.has(token))
            intersection++;
    }
    const union = setA.size + setB.size - intersection;
    return union === 0 ? 1.0 : intersection / union;
}
/** Ordered comparison: matching positions / max length. */
function orderedSimilarity(expected, actual) {
    if (expected.length === 0 && actual.length === 0)
        return 1.0;
    const maxLen = Math.max(expected.length, actual.length);
    let matches = 0;
    for (let i = 0; i < Math.min(expected.length, actual.length); i++) {
        if (expected[i] === actual[i])
            matches++;
    }
    return matches / maxLen;
}
/** Set intersection / set union. */
function setSimilarity(expected, actual) {
    const setA = new Set(expected);
    const setB = new Set(actual);
    if (setA.size === 0 && setB.size === 0)
        return 1.0;
    let intersection = 0;
    for (const item of setA) {
        if (setB.has(item))
            intersection++;
    }
    const union = setA.size + setB.size - intersection;
    return union === 0 ? 1.0 : intersection / union;
}
function classifySeverity(similarity) {
    if (similarity >= 0.95)
        return 'none';
    if (similarity >= 0.8)
        return 'minor';
    if (similarity >= 0.5)
        return 'major';
    return 'critical';
}
// ─── Public API ──────────────────────────────────────────────────────
async function saveGoldenTrace(projectDir, trace) {
    const dir = tracesDir(projectDir);
    await fs.mkdir(dir, { recursive: true });
    await fs.writeFile(traceFile(projectDir, trace.id), JSON.stringify(trace, null, 2), 'utf-8');
}
async function loadGoldenTrace(projectDir, id) {
    try {
        const raw = await fs.readFile(traceFile(projectDir, id), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return undefined;
    }
}
async function listGoldenTraces(projectDir) {
    const dir = tracesDir(projectDir);
    let entries;
    try {
        entries = await fs.readdir(dir);
    }
    catch {
        return [];
    }
    const traces = [];
    for (const entry of entries) {
        if (!entry.endsWith('.json'))
            continue;
        try {
            const raw = await fs.readFile(path.join(dir, entry), 'utf-8');
            traces.push(JSON.parse(raw));
        }
        catch {
            // skip malformed files
        }
    }
    return traces;
}
async function deleteGoldenTrace(projectDir, id) {
    try {
        await fs.unlink(traceFile(projectDir, id));
        return true;
    }
    catch {
        return false;
    }
}
function compareWithGolden(trace, actual) {
    const outputSim = jaccardSimilarity(trace.expectedOutput, actual.output);
    const toolSim = orderedSimilarity(trace.expectedToolCalls, actual.toolCalls);
    const fileSim = setSimilarity(trace.expectedFiles, actual.files);
    const w = trace.similarityWeights ?? { output: 0.5, tools: 0.3, files: 0.2 };
    const similarity = outputSim * w.output + toolSim * w.tools + fileSim * w.files;
    const diffs = [];
    if (outputSim < 1.0) {
        diffs.push({
            field: 'output',
            expected: trace.expectedOutput,
            actual: actual.output,
            detail: `Output similarity: ${(outputSim * 100).toFixed(1)}%`,
        });
    }
    if (toolSim < 1.0) {
        const missing = trace.expectedToolCalls.filter((t, i) => actual.toolCalls[i] !== t);
        const extra = actual.toolCalls.filter((t, i) => trace.expectedToolCalls[i] !== t);
        diffs.push({
            field: 'tool_calls',
            expected: JSON.stringify(trace.expectedToolCalls),
            actual: JSON.stringify(actual.toolCalls),
            detail: `Tool similarity: ${(toolSim * 100).toFixed(1)}%. Missing: [${missing.join(', ')}]. Extra: [${extra.join(', ')}]`,
        });
    }
    if (fileSim < 1.0) {
        const expectedSet = new Set(trace.expectedFiles);
        const actualSet = new Set(actual.files);
        const missing = trace.expectedFiles.filter(f => !actualSet.has(f));
        const extra = actual.files.filter(f => !expectedSet.has(f));
        diffs.push({
            field: 'files',
            expected: JSON.stringify(trace.expectedFiles),
            actual: JSON.stringify(actual.files),
            detail: `File similarity: ${(fileSim * 100).toFixed(1)}%. Missing: [${missing.join(', ')}]. Extra: [${extra.join(', ')}]`,
        });
    }
    return {
        traceId: trace.id,
        traceName: trace.name,
        severity: classifySeverity(similarity),
        diffs,
        similarity,
    };
}
function captureFromRun(name, model, input, output, toolCalls, files, opts) {
    return {
        id: crypto.randomUUID(),
        name,
        capturedAt: new Date().toISOString(),
        model,
        input,
        expectedOutput: output,
        expectedToolCalls: toolCalls,
        expectedFiles: files,
        tags: opts?.tags ?? [],
        notes: opts?.notes,
    };
}
function renderDriftReport(results) {
    if (results.length === 0) {
        return '# Drift Report\n\nNo traces compared.\n';
    }
    const lines = ['# Drift Report', ''];
    for (const r of results) {
        lines.push(`## ${r.traceName} (\`${r.traceId}\`)`);
        lines.push('');
        lines.push(`- **Severity:** ${r.severity}`);
        lines.push(`- **Similarity:** ${(r.similarity * 100).toFixed(1)}%`);
        if (r.diffs.length === 0) {
            lines.push('- No differences detected.');
        }
        else {
            lines.push('');
            lines.push('### Diffs');
            lines.push('');
            for (const d of r.diffs) {
                lines.push(`- **${d.field}**: ${d.detail}`);
            }
        }
        lines.push('');
    }
    return lines.join('\n');
}
//# sourceMappingURL=goldenTraces.js.map