"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssistantService = void 0;
const nanoid_1 = require("nanoid");
const stage1Contracts_1 = require("../types/stage1Contracts");
const config_1 = require("../config");
const log_1 = require("../utils/log");
const answerComposer_1 = require("./answerComposer");
const assistantDataLayer_1 = require("./assistantDataLayer");
const assistantSessionLogger_1 = require("./assistantSessionLogger");
const investigationState_1 = require("./investigationState");
const retrievalResultNormalizer_1 = require("./retrievalResultNormalizer");
function retrievalSummaryForRoute(route) {
    if (route === "store_canonical")
        return "Canonical accounting data path selected.";
    if (route === "store_feature_risk")
        return "Risk/control profile path selected.";
    if (route === "hybrid_store_plus_live")
        return "Hybrid chain analysis path selected.";
    if (route === "live_mcp_drilldown")
        return "Live drilldown path selected.";
    if (route === "batch_refresh_then_store")
        return "Heavy analytical batch path selected.";
    return "Route selected.";
}
function mapNoRouteReason(reason) {
    if (reason === "out_of_scope")
        return "Fragment out of scope.";
    if (reason === "insufficient_specificity")
        return "Needs clarification.";
    if (reason === "missing_mapping")
        return "Route mapping is missing.";
    if (reason === "unsupported_fragment_type")
        return "Fragment type unsupported.";
    return "No-route decision.";
}
function extractFragments(normalized) {
    if (!normalized || typeof normalized !== "object") {
        return [];
    }
    const source = normalized;
    return Array.isArray(source.fragments) ? source.fragments : [];
}
function extractExecutionState(normalized) {
    const fragments = extractFragments(normalized);
    return fragments.map((item) => {
        if (!item || typeof item !== "object") {
            return {};
        }
        const fragment = item;
        return {
            fragment_id: fragment.fragment_id ?? null,
            execution_readiness: fragment.execution_readiness ?? null,
            route_status: fragment.route_status ?? null,
            no_route_reason: fragment.no_route_reason ?? null
        };
    });
}
function fragmentTextById(normalized) {
    const result = new Map();
    for (const item of extractFragments(normalized)) {
        if (!item || typeof item !== "object") {
            continue;
        }
        const fragment = item;
        const fragmentId = typeof fragment.fragment_id === "string" ? fragment.fragment_id : "";
        if (!fragmentId) {
            continue;
        }
        const text = (typeof fragment.raw_fragment_text === "string" && fragment.raw_fragment_text.trim()) ||
            (typeof fragment.normalized_fragment_text === "string" && fragment.normalized_fragment_text.trim()) ||
            "";
        result.set(fragmentId, text);
    }
    return result;
}
function extractDiscardedIntentSegments(normalized) {
    if (!normalized || typeof normalized !== "object") {
        return [];
    }
    const source = normalized;
    if (!Array.isArray(source.discarded_fragments)) {
        return [];
    }
    return source.discarded_fragments
        .map((item) => {
        if (!item || typeof item !== "object") {
            return null;
        }
        const value = item;
        const text = typeof value.raw_fragment_text === "string" ? value.raw_fragment_text.trim() : "";
        return text || null;
    })
        .filter((item) => Boolean(item));
}
function collectDateSpans(text) {
    const spans = [];
    const datePattern = /\b20\d{2}[-/.](?:0[1-9]|1[0-2])(?:[-/.](?:0[1-9]|[12]\d|3[01]))?\b/g;
    let match = null;
    while ((match = datePattern.exec(text)) !== null) {
        spans.push({
            start: match.index,
            end: match.index + match[0].length
        });
    }
    return spans;
}
function intersectsAnySpan(start, end, spans) {
    return spans.some((span) => start < span.end && end > span.start);
}
function extractAccountTokens(text) {
    const lower = String(text ?? "").toLowerCase();
    const explicitAccounts = new Set();
    const contextualPattern = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b)\s*(?:№|#|:)?\s*(\d{2}(?:\.\d{2})?)/giu;
    let contextual = null;
    while ((contextual = contextualPattern.exec(lower)) !== null) {
        if (contextual[1]) {
            explicitAccounts.add(contextual[1]);
        }
    }
    if (explicitAccounts.size > 0) {
        return Array.from(explicitAccounts);
    }
    const spans = collectDateSpans(lower);
    const accountList = [];
    const genericPattern = /\b\d{2}(?:\.\d{2})?\b/g;
    let generic = null;
    while ((generic = genericPattern.exec(lower)) !== null) {
        const value = generic[0];
        const start = generic.index;
        const end = start + value.length;
        if (intersectsAnySpan(start, end, spans)) {
            continue;
        }
        accountList.push(value);
    }
    return Array.from(new Set(accountList));
}
function extractSubjectTokens(text) {
    const lower = text.toLowerCase();
    const tokens = [];
    const push = (token, match) => {
        if (match)
            tokens.push(token);
    };
    push("nds", /\b(?:\u043d\u0434\u0441|vat)\b/iu.test(lower));
    push("os", /(?:\b\u043e\u0441\b|\u043e\u0441\u043d\u043e\u0432\u043d(?:\u044b\u0435|\u044b\u0445)\s+\u0441\u0440\u0435\u0434|osnovn(?:ye|yh)\s+sred)/iu.test(lower));
    push("counterparty", /(?:\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|supplier|buyer|counterparty|kontragent|postavshch|pokupatel)/iu.test(lower));
    push("document", /(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446|\u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d|\u0432\u044b\u043f\u0438\u0441\u043a|\u043f\u043b\u0430\u0442\u0435\u0436|document|invoice|posting|dokument|realiz|postuplen|vypisk|platezh)/iu.test(lower));
    push("saldo", /(?:\u0441\u0430\u043b\u044c\u0434\u043e|\u043e\u0441\u0442\u0430\u0442\u043a|saldo|balance)/iu.test(lower));
    push("anomaly", /(?:\u0430\u043d\u043e\u043c\u0430\u043b|\u0440\u0438\u0441\u043a|\u043f\u043e\u0434\u043e\u0437\u0440|\u0445\u0432\u043e\u0441\u0442|anomaly|risk|anomali|hvost|tail)/iu.test(lower));
    push("chain", /(?:\u0446\u0435\u043f\u043e\u0447|\u0440\u0430\u0437\u043b\u043e\u0436|\u0441\u0432\u044f\u0437\u043a|\u0440\u0430\u0437\u0440\u044b\u0432|chain|razlozh|svyaz|razryv)/iu.test(lower));
    const accountMatches = extractAccountTokens(lower);
    for (const account of accountMatches) {
        tokens.push(`account_${account}`);
    }
    return Array.from(new Set(tokens));
}
function extractRequirements(routeSummary, normalized, userMessage) {
    const fragmentText = fragmentTextById(normalized);
    const byFragment = new Map();
    const requirements = [];
    const pushRequirement = (input) => {
        const subjectTokens = extractSubjectTokens(input.requirement_text);
        requirements.push({
            requirement_id: input.requirement_id,
            source_fragment_id: input.source_fragment_id,
            requirement_text: input.requirement_text,
            subject_tokens: subjectTokens,
            status: input.status,
            route: input.route
        });
        if (input.source_fragment_id) {
            const current = byFragment.get(input.source_fragment_id) ?? [];
            current.push(input.requirement_id);
            byFragment.set(input.source_fragment_id, current);
        }
    };
    if (!routeSummary) {
        pushRequirement({
            requirement_id: "R1",
            source_fragment_id: null,
            requirement_text: userMessage,
            status: "clarification_needed",
            route: null
        });
        return { requirements, byFragment };
    }
    if (routeSummary.mode === "legacy_v1") {
        pushRequirement({
            requirement_id: "R1",
            source_fragment_id: "F1",
            requirement_text: userMessage,
            status: "covered",
            route: routeSummary.route_hint
        });
        return { requirements, byFragment };
    }
    routeSummary.decisions.forEach((decision, index) => {
        const requirementId = `R${index + 1}`;
        const text = fragmentText.get(decision.fragment_id) ?? userMessage;
        let status = "covered";
        if (decision.route === "no_route") {
            if (decision.no_route_reason === "out_of_scope") {
                status = "out_of_scope";
            }
            else if (decision.no_route_reason === "insufficient_specificity") {
                status = "clarification_needed";
            }
            else {
                status = "uncovered";
            }
        }
        pushRequirement({
            requirement_id: requirementId,
            source_fragment_id: decision.fragment_id,
            requirement_text: text,
            status,
            route: decision.route === "no_route" ? null : decision.route
        });
    });
    return { requirements, byFragment };
}
function toExecutionPlan(routeSummary, normalized, userMessage, requirementByFragment) {
    if (!routeSummary) {
        return [];
    }
    const fragmentText = fragmentTextById(normalized);
    if (routeSummary.mode === "legacy_v1") {
        return [
            {
                fragment_id: "F1",
                requirement_ids: requirementByFragment.get("F1") ?? ["R1"],
                route: routeSummary.route_hint,
                should_execute: true,
                fragment_text: userMessage,
                no_route_reason: null,
                clarification_reason: null
            }
        ];
    }
    return routeSummary.decisions.map((decision) => {
        const text = fragmentText.get(decision.fragment_id) ?? userMessage;
        if (decision.route === "no_route") {
            return {
                fragment_id: decision.fragment_id,
                requirement_ids: requirementByFragment.get(decision.fragment_id) ?? [],
                route: "no_route",
                should_execute: false,
                fragment_text: text,
                no_route_reason: decision.no_route_reason ?? null,
                clarification_reason: decision.clarification_reason ?? null
            };
        }
        return {
            fragment_id: decision.fragment_id,
            requirement_ids: requirementByFragment.get(decision.fragment_id) ?? [],
            route: decision.route,
            should_execute: true,
            fragment_text: text,
            no_route_reason: null,
            clarification_reason: decision.clarification_reason ?? null
        };
    });
}
function toDebugRoutes(routeSummary) {
    if (!routeSummary) {
        return [];
    }
    if (routeSummary.mode === "legacy_v1") {
        return [
            {
                fragment_id: "F1",
                route: routeSummary.route_hint,
                reason: retrievalSummaryForRoute(routeSummary.route_hint),
                confidence: routeSummary.confidence,
                intent_class: routeSummary.intent_class
            }
        ];
    }
    return routeSummary.decisions.map((decision) => ({
        fragment_id: decision.fragment_id,
        route: decision.route,
        reason: decision.reason,
        route_status: decision.route_status ?? null,
        no_route_reason: decision.no_route_reason ?? null,
        clarification_reason: decision.clarification_reason ?? null,
        execution_readiness: decision.execution_readiness ?? null
    }));
}
function buildSkippedResult(item) {
    return (0, retrievalResultNormalizer_1.normalizeRetrievalResult)(item.fragment_id, item.requirement_ids, "no_route", {
        status: "empty",
        result_type: "summary",
        items: [],
        summary: {
            skipped: true,
            reason: mapNoRouteReason(item.no_route_reason),
            no_route_reason: item.no_route_reason,
            clarification_reason: item.clarification_reason
        },
        evidence: [],
        why_included: [],
        selection_reason: [mapNoRouteReason(item.no_route_reason)],
        risk_factors: [],
        business_interpretation: ["Р”Р°РЅРЅС‹Р№ С„СЂР°РіРјРµРЅС‚ РЅРµ Р±С‹Р» РІС‹РїРѕР»РЅРµРЅ РёР·-Р·Р° no-route СЂРµС€РµРЅРёСЏ."],
        confidence: "low",
        limitations: ["Р¤СЂР°РіРјРµРЅС‚ С‚СЂРµР±СѓРµС‚ СѓС‚РѕС‡РЅРµРЅРёСЏ РёР»Рё РѕС‚СЃСѓС‚СЃС‚РІСѓРµС‚ РїРѕРґРґРµСЂР¶РёРІР°РµРјС‹Р№ РјР°СЂС€СЂСѓС‚."],
        errors: []
    });
}
function summarizeUnique(values, limit = 6) {
    return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))).slice(0, limit);
}
const SUBJECT_TOKEN_RULES = {
    nds: {
        critical: true,
        patterns: [
            "vat",
            "accumulationregister",
            "\u043d\u0434\u0441",
            "\u043a\u043d\u0438\u0433\u0438\u043f\u043e\u043a\u0443\u043f\u043e\u043a",
            "\u043a\u043d\u0438\u0433\u0438\u043f\u0440\u043e\u0434\u0430\u0436",
            "\u043d\u0430\u043b\u043e\u0433\u043d\u0430\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u0443\u044e\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c"
        ]
    },
    os: {
        critical: true,
        patterns: ["fixed_asset", "fixedasset", "\u043e\u0441\u043d\u043e\u0432\u043d", "\u0430\u043c\u043e\u0440\u0442\u0438\u0437"]
    },
    saldo: {
        critical: true,
        patterns: ["balance", "saldo", "\u0441\u0430\u043b\u044c\u0434\u043e", "\u043e\u0441\u0442\u0430\u0442"]
    },
    counterparty: {
        critical: false,
        patterns: [
            "counterparty",
            "supplier",
            "buyer",
            "counterparty_id",
            "journal_counterparty",
            "document_has_counterparty",
            "\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442",
            "\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a",
            "\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b"
        ],
        routes: ["hybrid_store_plus_live", "store_feature_risk", "store_canonical"]
    },
    document: {
        critical: false,
        patterns: [
            "document",
            "recorder",
            "journal",
            "document_refs_count",
            "recorded_by_document",
            "journal_refers_to_document",
            "\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442"
        ],
        routes: ["hybrid_store_plus_live", "store_feature_risk", "store_canonical", "live_mcp_drilldown"]
    },
    anomaly: {
        critical: false,
        patterns: [
            "risk",
            "risk_score",
            "unknown_link_count",
            "zero_guid",
            "navigation_links",
            "missing_counterparty_link",
            "\u0430\u043d\u043e\u043c\u0430\u043b",
            "\u0440\u0438\u0441\u043a"
        ],
        routes: ["store_feature_risk", "batch_refresh_then_store"]
    },
    chain: {
        critical: false,
        patterns: ["chain", "cross_entity_chain", "relation_types", "operations_count", "matched_counterparties", "\u0446\u0435\u043f\u043e\u0447"],
        routes: ["hybrid_store_plus_live"]
    }
};
function hasRegexMatch(corpus, pattern) {
    try {
        return pattern.test(corpus);
    }
    catch {
        return false;
    }
}
function evaluateSubjectTokenMatch(token, corpus, executedRoutes) {
    if (token.startsWith("account_")) {
        const account = token.slice("account_".length).trim();
        if (!account) {
            return { matched: false, critical: true };
        }
        const escaped = account.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
        const accountPattern = new RegExp(`(^|[^0-9])${escaped}([^0-9]|$)`, "i");
        return { matched: hasRegexMatch(corpus, accountPattern), critical: true };
    }
    const rule = SUBJECT_TOKEN_RULES[token];
    if (rule) {
        const byPattern = rule.patterns.some((pattern) => corpus.includes(pattern));
        const byRoute = Array.isArray(rule.routes) ? rule.routes.some((route) => executedRoutes.has(route)) : false;
        return { matched: byPattern || byRoute, critical: rule.critical };
    }
    return { matched: corpus.includes(token), critical: false };
}
function evaluateCoverage(requirements, retrievalResults) {
    const statusByRequirement = new Map();
    for (const result of retrievalResults) {
        for (const requirementId of result.requirement_ids) {
            const list = statusByRequirement.get(requirementId) ?? [];
            list.push(result.status);
            statusByRequirement.set(requirementId, list);
        }
    }
    const resolvedRequirements = requirements.map((requirement) => {
        if (requirement.status === "out_of_scope" || requirement.status === "clarification_needed") {
            return requirement;
        }
        const statuses = statusByRequirement.get(requirement.requirement_id) ?? [];
        if (statuses.length === 0) {
            return { ...requirement, status: "uncovered" };
        }
        if (statuses.includes("ok")) {
            return { ...requirement, status: "covered" };
        }
        if (statuses.includes("partial")) {
            return { ...requirement, status: "partially_covered" };
        }
        if (statuses.includes("empty") && !statuses.includes("error")) {
            return { ...requirement, status: "covered" };
        }
        return { ...requirement, status: "uncovered" };
    });
    const requirementsCovered = resolvedRequirements.filter((item) => item.status === "covered").length;
    const requirementsUncovered = resolvedRequirements
        .filter((item) => item.status === "uncovered")
        .map((item) => item.requirement_id);
    const requirementsPartiallyCovered = resolvedRequirements
        .filter((item) => item.status === "partially_covered")
        .map((item) => item.requirement_id);
    const clarificationNeededFor = resolvedRequirements
        .filter((item) => item.status === "clarification_needed")
        .map((item) => item.requirement_id);
    const outOfScopeRequirements = resolvedRequirements
        .filter((item) => item.status === "out_of_scope")
        .map((item) => item.requirement_id);
    return {
        requirements: resolvedRequirements,
        coverage: {
            requirements_total: resolvedRequirements.length,
            requirements_covered: requirementsCovered,
            requirements_uncovered: requirementsUncovered,
            requirements_partially_covered: requirementsPartiallyCovered,
            clarification_needed_for: clarificationNeededFor,
            out_of_scope_requirements: outOfScopeRequirements
        }
    };
}
function checkGrounding(userMessage, requirements, coverage, retrievalResults) {
    const whyIncludedSummary = summarizeUnique(retrievalResults.flatMap((item) => item.why_included));
    const selectionReasonSummary = summarizeUnique(retrievalResults.flatMap((item) => item.selection_reason));
    const hasMaterialResults = retrievalResults.some((item) => item.status === "ok" || item.status === "partial");
    const subjectTokens = extractSubjectTokens(userMessage);
    const executedRoutes = new Set(retrievalResults
        .filter((item) => item.status !== "error")
        .map((item) => item.route)
        .filter(Boolean));
    const retrievalCorpus = JSON.stringify(retrievalResults.map((item) => ({
        route: item.route,
        result_type: item.result_type,
        summary: item.summary,
        items: item.items,
        evidence: item.evidence,
        why_included: item.why_included,
        selection_reason: item.selection_reason,
        risk_factors: item.risk_factors,
        business_interpretation: item.business_interpretation
    }))).toLowerCase();
    const missingSubjectTokens = [];
    const missingCriticalTokens = [];
    for (const token of subjectTokens) {
        const match = evaluateSubjectTokenMatch(token, retrievalCorpus, executedRoutes);
        if (!match.matched) {
            missingSubjectTokens.push(token);
            if (match.critical) {
                missingCriticalTokens.push(token);
            }
        }
    }
    const onlyAccountCriticalMissing = missingCriticalTokens.length > 0 && missingCriticalTokens.every((token) => token.startsWith("account_"));
    const accountOnlyMismatchRecoverable = hasMaterialResults &&
        coverage.requirements_covered > 0 &&
        onlyAccountCriticalMissing &&
        (whyIncludedSummary.length > 0 || selectionReasonSummary.length > 0);
    const routeSubjectMatch = !hasMaterialResults || missingCriticalTokens.length === 0 || accountOnlyMismatchRecoverable;
    let status = "grounded";
    const reasons = [];
    if (!routeSubjectMatch) {
        status = "route_mismatch_blocked";
        reasons.push(`РќРµ РїРѕРґС‚РІРµСЂР¶РґРµРЅС‹ РєСЂРёС‚РёС‡РЅС‹Рµ РїСЂРµРґРјРµС‚РЅС‹Рµ С‚РѕРєРµРЅС‹ Р·Р°РїСЂРѕСЃР°: ${missingCriticalTokens.join(", ")}`);
    }
    else if (accountOnlyMismatchRecoverable) {
        status = "partial";
        reasons.push(`Р С‡РµС‚-С‚РѕРєРµРЅС‹ РЅРµ РїРѕРґС‚РІРµСЂР¶РґРµРЅС‹ РЅР°РїСЂСЏРјСѓСЋ (${missingCriticalTokens.join(", ")}), РЅРѕ РµСЃС‚СЊ СЂРµР»РµРІР°РЅС‚РЅР°СЏ РѕРїРѕСЂР° РґР»СЏ РѕРіСЂР°РЅРёС‡РµРЅРЅРѕРіРѕ РІС‹РІРѕРґР°.`);
    }
    else if (coverage.requirements_covered === 0) {
        status = "no_grounded_answer";
        reasons.push("РќРё РѕРґРЅРѕ С‚СЂРµР±РѕРІР°РЅРёРµ РЅРµ РїРѕР»СѓС‡РёР»Рѕ РїРѕРґС‚РІРµСЂР¶РґРµРЅРЅРѕРіРѕ РїРѕРєСЂС‹С‚РёСЏ.");
    }
    else if (coverage.requirements_uncovered.length > 0 ||
        coverage.requirements_partially_covered.length > 0 ||
        coverage.clarification_needed_for.length > 0 ||
        coverage.out_of_scope_requirements.length > 0) {
        status = "partial";
        reasons.push("Р’РѕРїСЂРѕСЃ РїРѕРєСЂС‹С‚ С‡Р°СЃС‚РёС‡РЅРѕ: РµСЃС‚СЊ РЅРµРїРѕРєСЂС‹С‚С‹Рµ РёР»Рё С‚СЂРµР±СѓСЋС‰РёРµ СѓС‚РѕС‡РЅРµРЅРёСЏ С‚СЂРµР±РѕРІР°РЅРёСЏ.");
    }
    if (whyIncludedSummary.length === 0) {
        reasons.push("РќРµС‚ explainable-СЃРёРіРЅР°Р»РѕРІ why_included РІ СЂРµР·СѓР»СЊС‚Р°С‚Р°С… РІС‹Р±РѕСЂРєРё.");
    }
    if (missingSubjectTokens.length > 0 && missingCriticalTokens.length === 0) {
        reasons.push(`Р§Р°СЃС‚СЊ РєРѕРЅС‚РµРєСЃС‚РЅС‹С… С‚РѕРєРµРЅРѕРІ РЅРµ РїРѕРґС‚РІРµСЂР¶РґРµРЅР° РЅР°РїСЂСЏРјСѓСЋ: ${missingSubjectTokens.join(", ")}`);
    }
    const missingRequirements = [
        ...coverage.requirements_uncovered,
        ...coverage.requirements_partially_covered,
        ...coverage.clarification_needed_for,
        ...coverage.out_of_scope_requirements
    ];
    return {
        status,
        route_subject_match: routeSubjectMatch,
        missing_requirements: missingRequirements,
        reasons,
        why_included_summary: whyIncludedSummary,
        selection_reason_summary: selectionReasonSummary
    };
}
function firstNonEmptyLine(text) {
    const line = text
        .split("\n")
        .map((item) => item.trim())
        .find((item) => item.length > 0);
    return (line ?? text).slice(0, 220);
}
function buildClaimEvidenceLinks(retrievalResults) {
    const byClaim = new Map();
    for (const result of retrievalResults) {
        for (const evidence of result.evidence) {
            const claimRef = String(evidence.claim_ref ?? "").trim();
            if (!claimRef) {
                continue;
            }
            const evidenceId = String(evidence.evidence_id ?? "").trim();
            if (!evidenceId) {
                continue;
            }
            const current = byClaim.get(claimRef) ?? [];
            current.push(evidenceId);
            byClaim.set(claimRef, current);
        }
    }
    return Array.from(byClaim.entries())
        .slice(0, 10)
        .map(([claimRef, evidenceIds]) => ({
        claim_ref: claimRef,
        evidence_ids: summarizeUnique(evidenceIds, 10)
    }));
}
function buildAnswerStructureV11(input) {
    const evidenceIds = summarizeUnique(input.retrievalResults.flatMap((item) => item.evidence.map((evidence) => evidence.evidence_id)), 10);
    const mechanismNotes = summarizeUnique(input.retrievalResults.flatMap((item) => item.evidence
        .map((evidence) => evidence.mechanism_note)
        .filter((note) => typeof note === "string" && note.trim().length > 0)), 6);
    const sourceRefs = summarizeUnique(input.retrievalResults.flatMap((item) => item.evidence
        .map((evidence) => evidence.source_ref?.canonical_ref)
        .filter((value) => typeof value === "string" && value.trim().length > 0)), 8);
    const limitationReasonCodes = summarizeUnique(input.retrievalResults.flatMap((item) => item.evidence
        .flatMap((evidence) => {
        const code = evidence.limitation?.reason_code;
        return typeof code === "string" && code.trim().length > 0 ? [code] : [];
    })), 8);
    const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults);
    const limitations = summarizeUnique([...input.retrievalResults.flatMap((item) => item.limitations), ...input.groundingCheck.reasons], 8);
    const clarificationQuestions = input.coverageReport.clarification_needed_for.map((item) => `РЈС‚РѕС‡РЅРёС‚Рµ С‚СЂРµР±РѕРІР°РЅРёРµ ${item}.`);
    const recommendedActions = summarizeUnique([
        ...input.coverageReport.requirements_uncovered.map((item) => `РџСЂРѕРІРµСЂРёС‚СЊ РЅРµРїРѕРєСЂС‹С‚РѕРµ С‚СЂРµР±РѕРІР°РЅРёРµ ${item}.`),
        ...input.coverageReport.requirements_partially_covered.map((item) => `Р”РѕСѓС‚РѕС‡РЅРёС‚СЊ С‡Р°СЃС‚РёС‡РЅРѕ РїРѕРєСЂС‹С‚РѕРµ С‚СЂРµР±РѕРІР°РЅРёРµ ${item}.`)
    ], 6);
    const mechanismStatus = mechanismNotes.length === 0
        ? "unresolved"
        : limitationReasonCodes.includes("missing_mechanism") || limitationReasonCodes.includes("heuristic_inference")
            ? "limited"
            : "grounded";
    return {
        schema_version: stage1Contracts_1.ANSWER_STRUCTURE_SCHEMA_VERSION,
        answer_summary: firstNonEmptyLine(input.assistantReply),
        direct_answer: input.assistantReply,
        mechanism_block: {
            status: mechanismStatus,
            mechanism_notes: mechanismNotes,
            limitation_reason_codes: limitationReasonCodes
        },
        evidence_block: {
            evidence_ids: evidenceIds,
            source_refs: sourceRefs,
            mechanism_notes: mechanismNotes,
            coverage_note: input.coverageReport.requirements_total === input.coverageReport.requirements_covered
                ? "coverage_full_or_near_full"
                : "coverage_partial_or_limited",
            ...(config_1.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 && claimEvidenceLinks.length > 0
                ? {
                    claim_evidence_links: claimEvidenceLinks
                }
                : {})
        },
        uncertainty_block: {
            open_uncertainties: input.groundingCheck.missing_requirements,
            limitations
        },
        next_step_block: {
            recommended_actions: recommendedActions,
            clarification_questions: clarificationQuestions
        }
    };
}
const FOLLOWUP_ROUTE_HINTS = new Set(["store_canonical", "store_feature_risk", "hybrid_store_plus_live", "live_mcp_drilldown", "batch_refresh_then_store"]);
const FOLLOWUP_BUSINESS_CONTEXT_MAX = 320;
const FOLLOWUP_SUBJECT_MAX = 160;
const FOLLOWUP_QUESTION_APPEND_MAX = 260;
function compactWhitespace(value) {
    return value.replace(/\s+/g, " ").trim();
}
function hasAccountingSignal(text) {
    const lower = text.toLowerCase();
    if (/(?:^|[\s,;:])\d{2}(?:\.\d{2})?(?=$|[\s,.;:])/i.test(lower)) {
        return true;
    }
    return /(РїСЂРѕРІРѕРґРє|РґРѕРєСѓРјРµРЅС‚|СЂРµР°Р»РёР·Р°С†|РїРѕСЃС‚СѓРїР»РµРЅ|РІР·Р°РёРјРѕСЂР°СЃС‡РµС‚|СЃР°Р»СЊРґРѕ|РѕСЃС‚Р°С‚Рє|СЃС‡РµС‚|РЅРґСЃ|Р°РјРѕСЂС‚РёР·|СЂР±Рї|РѕСЃ|РєРѕРЅС‚СЂР°РіРµРЅС‚|РїРѕСЃС‚Р°РІС‰РёРє|РїРѕРєСѓРїР°С‚РµР»|РѕРїР»Р°С‚|Р±Р°РЅРє|РІС‹РїРёСЃРє|СЃРєР»Р°Рґ|С‚РѕРІР°СЂ|РјР°С‚РµСЂРёР°Р»|проводк|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|счёт|ндс|амортиз|рбп|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|закрыти|период|postavshchik|kontragent|schet|schetu|period|counterparty|supplier|invoice|posting|ledger|account|anomaly|risk)/i.test(lower);
}
function hasFollowupMarker(text) {
    const compact = compactWhitespace(text.toLowerCase());
    return /^(Рё|Р° РµС‰Рµ|Р° РµС‰С‘|РµС‰Рµ|РµС‰С‘|РґРѕР±Р°РІ|СѓС‚РѕС‡РЅ|РїСЂРѕРґРѕР»Р¶|С‚Р°РєР¶Рµ|и|а если|а еще|а ещё|еще|ещё|добав|уточн|продолж|также|plus|also|dobav|utochn|prodolzh)/i.test(compact);
}
function hasReferentialPointer(text) {
    return /(РїРѕ СЌС‚РѕРјСѓ|РїРѕ С‚РѕРјСѓ|СЌС‚Рѕ Р¶Рµ|СЌС‚РѕР№|СЌС‚РёРј|С‚РѕРјСѓ|по этому|по тому|это же|этой|этим|этому|из этого|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(text.toLowerCase());
}
function hasSmallTalkSignal(text) {
    return /(РїСЂРёРІРµС‚|РєР°Рє РґРµР»Р°|СЃРїР°СЃРёР±Рѕ|привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase());
}
function countTokens(text) {
    return compactWhitespace(text)
        .split(" ")
        .filter(Boolean).length;
}
function hasPeriodLiteral(text) {
    return /\b(20\d{2}(?:[-/.](?:0[1-9]|1[0-2]))?)\b/.test(text);
}
function extractNormalizedPeriodLiteral(text) {
    const monthly = text.match(/\b(20\d{2})[-/.](0[1-9]|1[0-2])\b/);
    if (monthly) {
        return `${monthly[1]}-${monthly[2]}`;
    }
    const yearly = text.match(/\b(20\d{2})\b/);
    if (yearly) {
        return yearly[1];
    }
    return null;
}
function hasStrongFollowupAnchors(userMessage, state) {
    const explicitPeriod = extractNormalizedPeriodLiteral(userMessage);
    if (explicitPeriod && state.focus.period && explicitPeriod !== state.focus.period) {
        const periodLooksLikeFollowupRefinement = hasFollowupMarker(userMessage) || hasReferentialPointer(userMessage);
        if (!periodLooksLikeFollowupRefinement) {
            return true;
        }
    }
    const explicitAccounts = extractAccountTokens(userMessage);
    if (explicitAccounts.length > 0) {
        const knownAccounts = new Set(state.focus.primary_accounts.map((item) => item.trim()));
        if (knownAccounts.size === 0) {
            return true;
        }
        if (explicitAccounts.some((item) => !knownAccounts.has(item))) {
            return true;
        }
    }
    return false;
}
function routeFromInvestigationState(state) {
    const rawDomain = compactWhitespace(state.focus.domain ?? "");
    if (!rawDomain) {
        return null;
    }
    if (FOLLOWUP_ROUTE_HINTS.has(rawDomain)) {
        return rawDomain;
    }
    for (const candidate of rawDomain.split(",").map((item) => compactWhitespace(item))) {
        if (FOLLOWUP_ROUTE_HINTS.has(candidate)) {
            return candidate;
        }
    }
    return null;
}
function withCappedLength(value, max) {
    return value.length <= max ? value : value.slice(0, max);
}
function mergeBusinessContext(existing, patchParts) {
    const existingText = compactWhitespace(existing ?? "");
    const patchText = compactWhitespace(patchParts.filter(Boolean).join("; "));
    if (!existingText && !patchText) {
        return undefined;
    }
    const merged = existingText && patchText ? `${existingText}; ${patchText}` : existingText || patchText;
    return withCappedLength(merged, FOLLOWUP_BUSINESS_CONTEXT_MAX);
}
function buildFollowupStateBinding(input) {
    const userMessage = String(input.userMessage ?? "").trim();
    if (!userMessage || input.investigationState.status !== "active" || input.investigationState.turn_index <= 0) {
        return {
            normalizedQuestion: userMessage,
            mergedContext: input.payloadContext,
            usage: null
        };
    }
    const strongSignal = hasAccountingSignal(userMessage);
    const followupMarker = hasFollowupMarker(userMessage);
    const referentialPointer = hasReferentialPointer(userMessage);
    const shortPrompt = countTokens(userMessage) <= 10;
    const smallTalkSignal = hasSmallTalkSignal(userMessage);
    const problemState = input.investigationState.problem_unit_state;
    const problemContinuityAvailable = config_1.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 &&
        Boolean(problemState) &&
        ((problemState?.active_problem_units.length ?? 0) > 0 || (problemState?.focus_problem_types.length ?? 0) > 0);
    const strongNewAnchorDetected = hasStrongFollowupAnchors(userMessage, input.investigationState);
    const periodRefinementFollowup = hasPeriodLiteral(userMessage) && problemContinuityAvailable;
    const shouldBind = !smallTalkSignal &&
        !strongNewAnchorDetected &&
        (followupMarker || referentialPointer || periodRefinementFollowup || (!strongSignal && shortPrompt));
    if (!shouldBind) {
        return {
            normalizedQuestion: userMessage,
            mergedContext: input.payloadContext,
            usage: null
        };
    }
    const context = {
        ...(input.payloadContext ?? {})
    };
    const hasExplicitExpectedRoute = Boolean(input.payloadContext?.expected_route);
    const expectedRouteFromState = !context?.expected_route ? routeFromInvestigationState(input.investigationState) : null;
    const periodHintFromState = !context?.period_hint ? input.investigationState.focus.period : null;
    if (expectedRouteFromState) {
        context.expected_route = expectedRouteFromState;
    }
    if (periodHintFromState) {
        context.period_hint = periodHintFromState;
    }
    const subject = withCappedLength(compactWhitespace(input.investigationState.focus.active_query_subject ?? ""), FOLLOWUP_SUBJECT_MAX);
    const businessContextPatch = ["followup_state_binding_v1"];
    let problemContinuityApplied = false;
    let problemContinuitySkippedReason = null;
    if (input.investigationState.focus.period) {
        businessContextPatch.push("active_period");
    }
    if (subject) {
        businessContextPatch.push(`focus_subject:${subject}`);
    }
    if (input.investigationState.focus.primary_accounts.length > 0) {
        businessContextPatch.push(`focus_accounts:${input.investigationState.focus.primary_accounts.join(",")}`);
    }
    if (problemContinuityAvailable) {
        if (hasExplicitExpectedRoute) {
            problemContinuitySkippedReason = "explicit_expected_route";
        }
        else {
            const focusTypes = (problemState?.focus_problem_types ?? []).slice(0, 3);
            const activeCount = problemState?.active_problem_units.length ?? 0;
            businessContextPatch.push("problem_unit_continuity_v1");
            if (focusTypes.length > 0) {
                businessContextPatch.push(`problem_focus_types:${focusTypes.join(",")}`);
            }
            businessContextPatch.push(`problem_active_count:${activeCount}`);
            problemContinuityApplied = true;
        }
    }
    const mergedBusinessContext = mergeBusinessContext(context?.business_context, businessContextPatch);
    if (mergedBusinessContext) {
        context.business_context = mergedBusinessContext;
    }
    const shouldAugmentQuestion = Boolean(subject) && (followupMarker || referentialPointer || !strongSignal);
    let normalizedQuestion = userMessage;
    if (shouldAugmentQuestion) {
        const appendParts = [`Р¤РѕРєСѓСЃ С‚РµРєСѓС‰РµРіРѕ СЂР°Р·Р±РѕСЂР°: ${subject}`];
        if (input.investigationState.focus.primary_accounts.length > 0 && !/\b\d{2}(?:\.\d{2})?\b/.test(userMessage)) {
            appendParts.push(`РЎС‡РµС‚Р° С„РѕРєСѓСЃР°: ${input.investigationState.focus.primary_accounts.join(", ")}`);
        }
        if (periodHintFromState && !hasPeriodLiteral(userMessage)) {
            appendParts.push(`РџРµСЂРёРѕРґ С„РѕРєСѓСЃР°: ${periodHintFromState}`);
        }
        if (problemContinuityApplied && (problemState?.focus_problem_types.length ?? 0) > 0) {
            appendParts.push(`Problem focus types: ${(problemState?.focus_problem_types ?? []).slice(0, 3).join(", ")}`);
        }
        const appendBlock = withCappedLength(compactWhitespace(appendParts.join("; ")), FOLLOWUP_QUESTION_APPEND_MAX);
        normalizedQuestion = `${userMessage}\n${appendBlock}`.trim();
    }
    const reason = followupMarker
        ? "followup_marker"
        : referentialPointer
            ? "referential_pointer"
            : "underspecified_short_followup";
    return {
        normalizedQuestion,
        mergedContext: context,
        usage: {
            applied: true,
            reason,
            state_turn_index: input.investigationState.turn_index,
            context_patch: {
                period_hint_from_state: Boolean(periodHintFromState),
                expected_route_from_state: Boolean(expectedRouteFromState),
                business_context_from_state: Boolean(mergedBusinessContext),
                question_augmented: shouldAugmentQuestion,
                problem_continuity_available: problemContinuityAvailable,
                problem_continuity_applied: problemContinuityApplied,
                problem_continuity_skipped_reason: problemContinuityApplied ? null : problemContinuitySkippedReason,
                strong_new_anchor_detected: strongNewAnchorDetected
            }
        }
    };
}
function cloneItems(items) {
    return items.map((item) => ({
        ...item,
        debug: item.debug ? { ...item.debug } : null
    }));
}
class AssistantService {
    normalizerService;
    sessions;
    dataLayer;
    sessionLogger;
    constructor(normalizerService, sessions, dataLayer = new assistantDataLayer_1.AssistantDataLayer(), sessionLogger = new assistantSessionLogger_1.AssistantSessionLogger()) {
        this.normalizerService = normalizerService;
        this.sessions = sessions;
        this.dataLayer = dataLayer;
        this.sessionLogger = sessionLogger;
    }
    getSession(sessionId) {
        return this.sessions.getSession(sessionId);
    }
    async handleMessage(payload) {
        const session = this.sessions.ensureSession(payload.session_id);
        const sessionId = session.session_id;
        const userMessage = String(payload.user_message ?? payload.message ?? "").trim();
        const userItem = {
            message_id: `msg-${(0, nanoid_1.nanoid)(10)}`,
            session_id: sessionId,
            role: "user",
            text: userMessage,
            reply_type: null,
            created_at: new Date().toISOString(),
            trace_id: null,
            debug: null
        };
        this.sessions.appendItem(sessionId, userItem);
        const followupBinding = config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 &&
            config_1.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 &&
            session.investigation_state
            ? buildFollowupStateBinding({
                userMessage,
                payloadContext: payload.context,
                investigationState: session.investigation_state
            })
            : {
                normalizedQuestion: userMessage,
                mergedContext: payload.context,
                usage: null
            };
        const normalizePayload = {
            apiKey: payload.apiKey,
            model: payload.model,
            baseUrl: payload.baseUrl,
            temperature: payload.temperature,
            maxOutputTokens: payload.maxOutputTokens,
            promptVersion: payload.promptVersion ?? "normalizer_v2_0_2",
            systemPrompt: payload.systemPrompt,
            developerPrompt: payload.developerPrompt,
            domainPrompt: payload.domainPrompt,
            fewShotExamples: payload.fewShotExamples,
            userQuestion: followupBinding.normalizedQuestion,
            context: followupBinding.mergedContext,
            useMock: Boolean(payload.useMock)
        };
        const normalized = await this.normalizerService.normalize(normalizePayload);
        const requirementExtraction = extractRequirements(normalized.route_hint_summary, normalized.normalized, userMessage);
        const executionPlan = toExecutionPlan(normalized.route_hint_summary, normalized.normalized, userMessage, requirementExtraction.byFragment);
        const retrievalCalls = [];
        const retrievalResultsRaw = [];
        const retrievalResults = [];
        for (const planItem of executionPlan) {
            if (!planItem.should_execute) {
                retrievalCalls.push({
                    fragment_id: planItem.fragment_id,
                    requirement_ids: planItem.requirement_ids,
                    route: planItem.route,
                    status: "skipped",
                    query_text: planItem.fragment_text,
                    reason: mapNoRouteReason(planItem.no_route_reason)
                });
                retrievalResults.push(buildSkippedResult(planItem));
                continue;
            }
            retrievalCalls.push({
                fragment_id: planItem.fragment_id,
                requirement_ids: planItem.requirement_ids,
                route: planItem.route,
                status: "executed",
                query_text: planItem.fragment_text,
                reason: null
            });
            try {
                const raw = this.dataLayer.executeRoute(planItem.route, planItem.fragment_text);
                retrievalResultsRaw.push({
                    fragment_id: planItem.fragment_id,
                    route: planItem.route,
                    raw_result: raw
                });
                retrievalResults.push((0, retrievalResultNormalizer_1.normalizeRetrievalResult)(planItem.fragment_id, planItem.requirement_ids, planItem.route, raw));
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                retrievalCalls[retrievalCalls.length - 1].status = "failed";
                retrievalCalls[retrievalCalls.length - 1].reason = message;
                const rawError = {
                    status: "error",
                    result_type: "summary",
                    items: [],
                    summary: {
                        route: planItem.route
                    },
                    evidence: [],
                    why_included: [],
                    selection_reason: [],
                    risk_factors: [],
                    business_interpretation: [],
                    confidence: "low",
                    limitations: ["Route executor failed."],
                    errors: [message]
                };
                retrievalResultsRaw.push({
                    fragment_id: planItem.fragment_id,
                    route: planItem.route,
                    raw_result: rawError
                });
                retrievalResults.push((0, retrievalResultNormalizer_1.normalizeRetrievalResult)(planItem.fragment_id, planItem.requirement_ids, planItem.route, rawError));
            }
        }
        const coverageEvaluation = evaluateCoverage(requirementExtraction.requirements, retrievalResults);
        const groundingCheck = checkGrounding(userMessage, coverageEvaluation.requirements, coverageEvaluation.coverage, retrievalResults);
        const composition = (0, answerComposer_1.composeAssistantAnswer)({
            userMessage,
            routeSummary: normalized.route_hint_summary,
            retrievalResults,
            requirements: coverageEvaluation.requirements,
            coverageReport: coverageEvaluation.coverage,
            groundingCheck,
            enableAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11,
            enableProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1,
            enableLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1
        });
        const answerStructureV11 = config_1.FEATURE_ASSISTANT_CONTRACTS_V11
            ? config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11 && composition.answer_structure_v11
                ? composition.answer_structure_v11
                : buildAnswerStructureV11({
                    assistantReply: composition.assistant_reply,
                    coverageReport: coverageEvaluation.coverage,
                    groundingCheck,
                    retrievalResults
                })
            : null;
        const investigationStateSnapshot = config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 && session.investigation_state
            ? (0, investigationState_1.updateInvestigationState)({
                previous: session.investigation_state,
                timestamp: new Date().toISOString(),
                questionId: userItem.message_id,
                userMessage,
                routeSummary: normalized.route_hint_summary,
                requirements: coverageEvaluation.requirements,
                coverageReport: coverageEvaluation.coverage,
                retrievalResults,
                replyType: composition.reply_type
            })
            : null;
        if (config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 && investigationStateSnapshot) {
            this.sessions.setInvestigationState(sessionId, investigationStateSnapshot);
        }
        const debug = {
            trace_id: normalized.trace_id,
            prompt_version: normalized.prompt_version,
            schema_version: normalized.schema_version,
            fallback_type: composition.fallback_type,
            route_summary: normalized.route_hint_summary,
            fragments: extractFragments(normalized.normalized),
            requirements_extracted: coverageEvaluation.requirements,
            coverage_report: coverageEvaluation.coverage,
            routes: toDebugRoutes(normalized.route_hint_summary),
            retrieval_status: retrievalResults.map((item) => ({
                fragment_id: item.fragment_id,
                requirement_ids: item.requirement_ids,
                route: item.route,
                status: item.status,
                result_type: item.result_type
            })),
            retrieval_results: retrievalResults,
            answer_grounding_check: groundingCheck,
            dropped_intent_segments: extractDiscardedIntentSegments(normalized.normalized),
            ...(followupBinding.usage ? { followup_state_usage: followupBinding.usage } : {}),
            problem_centric_answer_applied: composition.problem_centric_answer_applied ?? false,
            problem_units_used_count: composition.problem_units_used_count ?? 0,
            problem_answer_mode: composition.problem_answer_mode ?? "stage1_policy_v11",
            ...(Array.isArray(composition.problem_unit_ids_used) && composition.problem_unit_ids_used.length > 0
                ? {
                    problem_unit_ids_used: composition.problem_unit_ids_used
                }
                : {}),
            answer_structure_v11: answerStructureV11,
            investigation_state_snapshot: investigationStateSnapshot,
            normalized: normalized.normalized
        };
        const assistantItem = {
            message_id: `msg-${(0, nanoid_1.nanoid)(10)}`,
            session_id: sessionId,
            role: "assistant",
            text: composition.assistant_reply,
            reply_type: composition.reply_type,
            created_at: new Date().toISOString(),
            trace_id: normalized.trace_id,
            debug
        };
        this.sessions.appendItem(sessionId, assistantItem);
        const current = this.sessions.getSession(sessionId);
        if (current) {
            this.sessionLogger.persistSession(current);
        }
        const conversation = cloneItems(current?.items ?? []);
        (0, log_1.logJson)({
            timestamp: new Date().toISOString(),
            level: "info",
            service: "assistant_loop",
            message: "assistant_message_processed",
            sessionId,
            eventType: "assistant_message",
            details: {
                session_id: sessionId,
                message_id: assistantItem.message_id,
                user_message: userMessage,
                normalizer_output: normalized.normalized,
                execution_plan: executionPlan,
                resolved_execution_state: extractExecutionState(normalized.normalized),
                routes: toDebugRoutes(normalized.route_hint_summary),
                retrieval_calls: retrievalCalls,
                retrieval_results_raw: retrievalResultsRaw,
                retrieval_results_normalized: retrievalResults,
                requirements_extracted: coverageEvaluation.requirements,
                requirements_total: coverageEvaluation.coverage.requirements_total,
                requirements_covered: coverageEvaluation.coverage.requirements_covered,
                requirements_uncovered: coverageEvaluation.coverage.requirements_uncovered,
                coverage_status: coverageEvaluation.coverage.requirements_total === coverageEvaluation.coverage.requirements_covered &&
                    coverageEvaluation.coverage.requirements_uncovered.length === 0 &&
                    coverageEvaluation.coverage.requirements_partially_covered.length === 0
                    ? "full"
                    : "partial_or_limited",
                answer_grounding_status: groundingCheck.status,
                reply_semantic_type: composition.reply_type,
                why_included_summary: groundingCheck.why_included_summary,
                selection_reason_summary: groundingCheck.selection_reason_summary,
                route_subject_match: groundingCheck.route_subject_match,
                clarification_target: coverageEvaluation.coverage.clarification_needed_for,
                dropped_intent_segments: extractDiscardedIntentSegments(normalized.normalized),
                ...(followupBinding.usage ? { followup_state_usage: followupBinding.usage } : {}),
                problem_centric_answer_applied: composition.problem_centric_answer_applied ?? false,
                problem_units_used_count: composition.problem_units_used_count ?? 0,
                problem_answer_mode: composition.problem_answer_mode ?? "stage1_policy_v11",
                ...(Array.isArray(composition.problem_unit_ids_used) && composition.problem_unit_ids_used.length > 0
                    ? {
                        problem_unit_ids_used: composition.problem_unit_ids_used
                    }
                    : {}),
                answer_structure_v11: answerStructureV11,
                investigation_state_snapshot: investigationStateSnapshot,
                fallback_type: composition.fallback_type,
                assistant_reply: composition.assistant_reply,
                reply_type: composition.reply_type,
                trace_id: normalized.trace_id
            }
        });
        return {
            ok: true,
            session_id: sessionId,
            assistant_reply: composition.assistant_reply,
            reply_type: composition.reply_type,
            conversation_item: assistantItem,
            debug,
            conversation
        };
    }
}
exports.AssistantService = AssistantService;
