/**
 * Decision logic for "should the worker suppress its transcript-driven
 * fallback emit for this Lark turn?"
 *
 * Pure function with no I/O — kept separate from worker.ts so the rules
 * (including the type-ahead window and the adopt-vs-non-adopt branching)
 * can be tested deterministically. The worker reads marker entries from
 * disk and threads them through here.
 *
 * Rules:
 *   - Adopt mode never suppresses: in /adopt the model in the adopted
 *     session is unaware of botmux, so transcript drain is the ONLY
 *     channel from model to Lark. There's no `botmux send` to compete
 *     with, hence no marker to gate on.
 *   - Non-adopt + isLocal: suppress. A local-typing turn means the
 *     attribution queue saw a user event whose content didn't match any
 *     pending Lark fingerprint. In a worker-spawned CLI that's a Web
 *     terminal hand-typed input — the user is already looking at it, no
 *     reason to push it back to the Lark thread.
 *   - Non-adopt + send observed in window: suppress. The window is
 *     [turn.markTimeMs, nextBoundaryMs). Legacy markers only carry time,
 *     so any marker in the window still suppresses. Newer markers carry the
 *     normalized length of the explicit `botmux send` body. When the
 *     transcript final is available, only emit fallback if that final is
 *     materially longer than any single explicit send in the same window.
 *     This lets short progress updates surface a later substantive final
 *     answer, while same-size rewrites and short acknowledgements stay
 *     suppressed. Boundary handling intentionally also considers
 *     queue items that haven't reached "ready" yet (passed in via
 *     nextBoundaryMs) — without that, a model that's still mid-tool-use
 *     for turn N+1 could leak a send credit into turn N's window.
 */
import { normaliseForFingerprint } from './bridge-turn-queue.js';
const MATERIAL_FINAL_LENGTH_RATIO = 2;
const MATERIAL_FINAL_MIN_EXTRA_CHARS = 120;
export function buildBridgeSendMarkerContent(content) {
    const normalized = normaliseForFingerprint(content);
    if (!normalized)
        return undefined;
    return { contentLength: normalized.length };
}
function hasStructuredContentMarker(marker) {
    return typeof marker.contentLength === 'number';
}
function finalIsMateriallyLongerThanSends(finalLength, markers) {
    const maxSentLength = markers.reduce((max, marker) => Math.max(max, marker.contentLength), 0);
    return finalLength >= maxSentLength * MATERIAL_FINAL_LENGTH_RATIO
        && finalLength - maxSentLength >= MATERIAL_FINAL_MIN_EXTRA_CHARS;
}
function markerSetCoversFinal(markers, finalText) {
    if (markers.length === 0)
        return false;
    // Back-compat: old marker files only have sentAtMs/messageId. Keep the old
    // conservative behavior for those entries instead of risking duplicates.
    if (markers.some(m => !hasStructuredContentMarker(m)))
        return true;
    const finalNormalized = normaliseForFingerprint(finalText ?? '');
    if (!finalNormalized)
        return true;
    const structuredMarkers = markers.filter(hasStructuredContentMarker);
    return !finalIsMateriallyLongerThanSends(finalNormalized.length, structuredMarkers);
}
export function shouldSuppressBridgeEmit(turn, nextBoundaryMs, markers, adoptMode) {
    if (adoptMode)
        return false;
    if (turn.isLocal)
        return true;
    if (turn.markTimeMs === undefined)
        return false;
    const lower = turn.markTimeMs;
    const upper = nextBoundaryMs ?? Number.POSITIVE_INFINITY;
    const markersInWindow = markers.filter(m => m.sentAtMs >= lower && m.sentAtMs < upper);
    return markerSetCoversFinal(markersInWindow, turn.finalText);
}
//# sourceMappingURL=bridge-fallback-gate.js.map