/**
 * V7 §2.4.6 — gather evidence for a decision-repair synthesis.
 *
 * Given a context hash + sessionId (and, optionally, an episodeId), we need
 * two sets of traces:
 *
 *   - **HIGH_VALUE**: recent traces in the same session with value > 0 whose
 *     agentText overlaps with the failing context. These drive the
 *     `preference` field.
 *   - **LOW_VALUE**: recent traces in the same session with value ≤ 0 (or
 *     explicit failure markers). These drive the `anti_pattern` field.
 *
 * The gather is a cheap SQL scan — no vector math — because the traces
 * we're interested in are always very recent. The orchestrator caps the
 * evidence at `evidenceLimit` per class.
 */
export function gatherRepairEvidence(input, deps) {
    const cap = input.limit ?? deps.config.evidenceLimit;
    const needle = input.keyword?.toLowerCase().trim() ?? "";
    const minLowValueThreshold = deps.config.minLowValueThreshold;
    // Pull a generous recent batch and split by value sign. Limiting at the
    // SQL layer is fine because the caller passes a small `limit`.
    const recent = deps.repos.traces.list({
        sessionId: input.sessionId,
        limit: Math.max(cap * 6, 24),
    });
    if (recent.length === 0) {
        return { highValue: [], lowValue: [] };
    }
    // Two-pass: first try with the keyword filter; if BOTH lists come back
    // empty, relax and rescan without the filter. This keeps the happy path
    // relevance-aware while still producing useful evidence when the
    // keyword is a normalized tool ID ("pip.install") that doesn't appear
    // verbatim in the natural-language trace text. If only one side is
    // empty we keep the filtered result — the synthesizer is designed to
    // fall back to template output in that case without needing extra
    // context.
    const firstPass = partition(recent, cap, needle, minLowValueThreshold);
    const firstPassEmpty = firstPass.highValue.length === 0 && firstPass.lowValue.length === 0;
    if (!needle || !firstPassEmpty) {
        deps.log.debug("evidence.gathered", {
            sessionId: input.sessionId,
            highValue: firstPass.highValue.length,
            lowValue: firstPass.lowValue.length,
            hadKeyword: Boolean(needle),
            relaxed: false,
        });
        return firstPass;
    }
    const relaxed = partition(recent, cap, "", minLowValueThreshold);
    deps.log.debug("evidence.gathered", {
        sessionId: input.sessionId,
        highValue: relaxed.highValue.length,
        lowValue: relaxed.lowValue.length,
        hadKeyword: Boolean(needle),
        relaxed: true,
    });
    return relaxed;
}
function partition(traces, cap, needle, minLowValueThreshold) {
    const highValue = [];
    const lowValue = [];
    for (const trace of traces) {
        if (needle && !traceContains(trace, needle))
            continue;
        if (trace.value > 0) {
            if (highValue.length < cap)
                highValue.push(trace);
        }
        else if (trace.value < -minLowValueThreshold || isFailureLike(trace)) {
            if (lowValue.length < cap)
                lowValue.push(trace);
        }
        if (highValue.length >= cap && lowValue.length >= cap)
            break;
    }
    return { highValue, lowValue };
}
function traceContains(trace, needle) {
    const blob = `${trace.userText}\n${trace.agentText}\n${trace.reflection ?? ""}`.toLowerCase();
    return blob.includes(needle);
}
function isFailureLike(trace) {
    const blob = `${trace.agentText}\n${trace.reflection ?? ""}`.toLowerCase();
    return (/(error|failed|failure|exception|traceback|timeout|retry)/.test(blob) ||
        trace.toolCalls.some((call) => Boolean(call.errorCode)));
}
/**
 * Truncate a trace trio to roughly `maxChars` preserving the tail (the
 * most recent lines) — the error messages we want to learn from usually
 * live at the end of the agentText.
 */
export function capTrace(trace, maxChars) {
    if (maxChars <= 0)
        return trace;
    return {
        ...trace,
        userText: tail(trace.userText, maxChars),
        agentText: tail(trace.agentText, maxChars),
        reflection: trace.reflection ? tail(trace.reflection, maxChars) : trace.reflection,
    };
}
function tail(s, n) {
    if (s.length <= n)
        return s;
    return `...${s.slice(s.length - n)}`;
}
//# sourceMappingURL=evidence.js.map