"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveClaimBoundAnchors = resolveClaimBoundAnchors; exports.applyTargetedEvidenceAcquisition = applyTargetedEvidenceAcquisition; const nanoid_1 = require("nanoid"); function uniqueStrings(values) { return Array.from(new Set(values.map((item) => String(item ?? "").trim()).filter(Boolean))); } function toObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value; } function normalizeTwoDigits(value) { return String(value).padStart(2, "0"); } function normalizeDateIso(value) { const raw = String(value ?? "").trim(); if (!raw) { return null; } const isoDay = raw.match(/\b(20\d{2})[-/.](0?[1-9]|1[0-2])[-/.](0?[1-9]|[12]\d|3[01])\b/); if (isoDay) { return `${isoDay[1]}-${normalizeTwoDigits(isoDay[2])}-${normalizeTwoDigits(isoDay[3])}`; } const isoMonth = raw.match(/\b(20\d{2})[-/.](0?[1-9]|1[0-2])\b/); if (isoMonth) { return `${isoMonth[1]}-${normalizeTwoDigits(isoMonth[2])}-01`; } const localDay = raw.match(/\b(0?[1-9]|[12]\d|3[01])[./-](0?[1-9]|1[0-2])[./-](\d{2}|\d{4})\b/); if (localDay) { const year = localDay[3].length === 2 ? `20${localDay[3]}` : localDay[3]; return `${year}-${normalizeTwoDigits(localDay[2])}-${normalizeTwoDigits(localDay[1])}`; } return null; } function isoToDate(value) { const normalized = normalizeDateIso(value); if (!normalized) { return null; } const date = new Date(`${normalized}T00:00:00Z`); return Number.isNaN(date.getTime()) ? null : date; } function formatDate(date) { const year = date.getUTCFullYear(); const month = normalizeTwoDigits(String(date.getUTCMonth() + 1)); const day = normalizeTwoDigits(String(date.getUTCDate())); return `${year}-${month}-${day}`; } function shiftDays(iso, deltaDays) { const date = isoToDate(iso); if (!date) { return null; } date.setUTCDate(date.getUTCDate() + deltaDays); return formatDate(date); } function accountPrefix(value) { const token = String(value ?? "").trim(); const match = token.match(/^(\d{2})/); return match ? match[1] : null; } function accountPrefixesFromAnchors(anchors) { const prefixes = new Set(); const accounts = Array.isArray(anchors?.accounts) ? anchors.accounts : []; for (const item of accounts) { const prefix = accountPrefix(String(item ?? "")); if (prefix) { prefixes.add(prefix); } } return prefixes; } function inferClaimType(input) { const lower = String(input.userMessage ?? "").toLowerCase(); const accountPrefixes = accountPrefixesFromAnchors(input.companyAnchors); const hasSettlementAccount = ["51", "60", "62", "76"].some((item) => accountPrefixes.has(item)); const hasVatAccount = ["19", "68"].some((item) => accountPrefixes.has(item)); const hasFixedAssetAccount = ["01", "02", "08"].some((item) => accountPrefixes.has(item)); const hasRbpAccount = accountPrefixes.has("97"); const hasMonthCloseAccount = ["20", "21", "23", "25", "26", "28", "29", "44"].some((item) => accountPrefixes.has(item)); const hasAdvanceSignal = /(?:advance|аванс|offset|зач[её]т|62\.02|60\.02)/i.test(lower); const hasSettlementLexical = /(?:долг|аванс|зач[её]т|взаимозач|расч[её]т|оплат|плате[жж]|платёж|постав|покупател|settlement|payment|supplier|customer)/i.test(lower); const hasVatLexical = /(?:\bvat\b|ндс|invoice|сч[её]т[- ]?фактур|register|книга\s+покупок|книга\s+продаж|книг[аи]\s+(?:покуп|продаж))/i.test(lower); const hasFixedAssetLexical = /(?:depreciat|amortization|fixed\s*asset|амортиз|основн(?:ые|ых)?\s+сред|объект\s+ос|сч[её]т\s*0[128]|account\s*0[128])/i.test(lower); const hasRbpLexical = /(?:\brbp\b|рбп|deferred\s*expense|writeoff|расходы\s+будущих\s+периодов|списани[ея]\s+рбп|account\s*97|сч[её]т\s*97)/i.test(lower); const hasMonthCloseLexical = /(?:month[- ]?close|закрыт|закрытие\s+месяца|косвен|account\s*20|account\s*44|сч[её]т\s*20|сч[её]т\s*44|распределен|period\s*close)/i.test(lower); if (input.focusDomainHint === "settlements_60_62") { return hasAdvanceSignal ? "prove_advance_offset_state" : "prove_settlement_closure_state"; } if (input.focusDomainHint === "vat_document_register_book") { return "prove_vat_chain_completeness"; } if (input.focusDomainHint === "fixed_asset_amortization") { return "prove_fixed_asset_amortization_coverage"; } if (input.focusDomainHint === "month_close_costs_20_44") { if (hasRbpLexical || hasRbpAccount) { return "prove_rbp_tail_state"; } return "prove_month_close_state"; } const settlementPriority = (hasSettlementLexical || hasSettlementAccount || hasAdvanceSignal) && !hasVatLexical && !hasFixedAssetLexical; const broadMonthClosePriority = (hasMonthCloseLexical || hasMonthCloseAccount) && !hasVatLexical && !hasVatAccount && !hasFixedAssetLexical && !hasFixedAssetAccount; if (hasAdvanceSignal && settlementPriority) { return "prove_advance_offset_state"; } if (settlementPriority) { return "prove_settlement_closure_state"; } if (hasVatLexical || (hasVatAccount && !settlementPriority)) { return "prove_vat_chain_completeness"; } if (broadMonthClosePriority) { return hasRbpLexical || hasRbpAccount ? "prove_rbp_tail_state" : "prove_month_close_state"; } if (hasFixedAssetLexical || (hasFixedAssetAccount && !settlementPriority && !hasVatLexical)) { return "prove_fixed_asset_amortization_coverage"; } if (hasRbpLexical || hasRbpAccount) { return "prove_rbp_tail_state"; } if (hasMonthCloseLexical || hasMonthCloseAccount) { return "prove_month_close_state"; } if (hasSettlementLexical || hasSettlementAccount) { return "prove_settlement_closure_state"; } return "prove_settlement_closure_state"; } function inferCounterpartyScope(message) { const lower = message.toLowerCase(); const out = []; if (/(?:supplier|vendor|поставщик|кредитор)/i.test(lower)) out.push("supplier"); if (/(?:customer|buyer|покупатель|дебитор)/i.test(lower)) out.push("customer"); return uniqueStrings(out); } function detectSignals(message) { const lower = message.toLowerCase(); return { hasAdvance: /(?:advance|аванс|offset|зач[её]т|62\.02|60\.02)/i.test(lower), hasClosure: /(?:close|closure|закрыт|хвост|tail|reconcile|зач[её]т)/i.test(lower), hasVat: /(?:\bvat\b|ндс|сч[её]т[- ]?фактур|invoice|книга\s+покупок|книга\s+продаж|register)/i.test(lower), hasMonthClose: /(?:month[- ]?close|закрытие\s+месяца|косвен|20\/44|account 20|account 44|сч[её]т 20|сч[её]т 44)/i.test(lower), hasRbp: /(?:\brbp\b|рбп|account 97|сч[её]т 97|writeoff|списани)/i.test(lower), hasFixedAsset: /(?:depreciat|amortization|fixed\s*asset|амортиз|основн(?:ые|ых)?\s+сред|объект\s+ос|сч[её]т\s*0[128]|account\s*0[128])/i.test(lower) }; } function resolveSettlementRole(input) { if (input.claimType !== "prove_settlement_closure_state" && input.claimType !== "prove_advance_offset_state") { return undefined; } const scopes = new Set(input.counterpartyScope.map((item) => String(item ?? "").trim().toLowerCase())); const lower = String(input.userMessage ?? "").toLowerCase(); const hasSupplierLexical = /(?:supplier|vendor|поставщ|кредитор|обязательств|payable)/i.test(lower); const hasCustomerLexical = /(?:customer|buyer|покупат|дебитор|receivable)/i.test(lower); const hasSupplierAccount = input.accountPrefixes.has("60"); const hasCustomerAccount = input.accountPrefixes.has("62"); const supplierSignal = scopes.has("supplier") || hasSupplierLexical || (hasSupplierAccount && !hasCustomerAccount); const customerSignal = scopes.has("customer") || hasCustomerLexical || (hasCustomerAccount && !hasSupplierAccount); if (supplierSignal && !customerSignal) { return "supplier"; } if (customerSignal && !supplierSignal) { return "customer"; } if (supplierSignal && customerSignal) { return "mixed"; } return "unknown"; } function mergeAnchors(anchors, key) { return uniqueStrings(Array.isArray(anchors?.[key]) ? anchors?.[key] : []); } function buildAllowedContextWindow(primaryPeriod) { if (!primaryPeriod) { return null; } const from = shiftDays(primaryPeriod.from, -365); const to = shiftDays(primaryPeriod.to, 365); if (!from || !to) { return null; } return { from, to, granularity: "month" }; } function missingFromRequired(required, resolved) { const missing = []; for (const anchor of required) { if (anchor === "counterparty_scope_or_contract") { if ((resolved.counterparty_scope?.length ?? 0) <= 0 && (resolved.contract?.length ?? 0) <= 0) { missing.push(anchor); } continue; } if (anchor === "settlement_object") { if ((resolved.contract?.length ?? 0) <= 0 && (resolved.document_numbers?.length ?? 0) <= 0) { missing.push(anchor); } continue; } if (anchor === "amount_or_document") { const hasAmount = (resolved.amounts?.length ?? 0) > 0; const hasDoc = (resolved.document_numbers?.length ?? 0) > 0 || (resolved.document_types?.length ?? 0) > 0; if (!hasAmount && !hasDoc) { missing.push(anchor); } continue; } if (anchor === "account_scope_or_document_type") { const hasAccount = (resolved.account_scope?.length ?? 0) > 0; const hasDocType = (resolved.document_types?.length ?? 0) > 0; if (!hasAccount && !hasDocType) { missing.push(anchor); } continue; } if ((resolved[anchor]?.length ?? 0) <= 0) { missing.push(anchor); } } return uniqueStrings(missing); } function resolveClaimBoundAnchors(input) { const claimType = inferClaimType({ userMessage: input.userMessage, focusDomainHint: input.focusDomainHint, companyAnchors: input.companyAnchors }); const signals = detectSignals(input.userMessage); const accountPrefixes = accountPrefixesFromAnchors(input.companyAnchors); const includeVatAnchors = claimType === "prove_vat_chain_completeness"; const includeMonthCloseAnchors = claimType === "prove_month_close_state"; const includeRbpAnchors = claimType === "prove_rbp_tail_state"; const includeFixedAssetAnchors = claimType === "prove_fixed_asset_amortization_coverage"; const hasVatSignal = signals.hasVat || accountPrefixes.has("19") || accountPrefixes.has("68"); const hasRbpSignal = signals.hasRbp || accountPrefixes.has("97"); const hasFixedAssetSignal = signals.hasFixedAsset || accountPrefixes.has("01") || accountPrefixes.has("02") || accountPrefixes.has("08"); const hasMonthCloseSignal = signals.hasMonthClose || accountPrefixes.has("20") || accountPrefixes.has("21") || accountPrefixes.has("23") || accountPrefixes.has("25") || accountPrefixes.has("26") || accountPrefixes.has("28") || accountPrefixes.has("29") || accountPrefixes.has("44"); const resolvedAnchors = { period: uniqueStrings([...mergeAnchors(input.companyAnchors, "periods"), ...mergeAnchors(input.companyAnchors, "dates")]), account_scope: mergeAnchors(input.companyAnchors, "accounts"), amounts: mergeAnchors(input.companyAnchors, "amounts"), contract: mergeAnchors(input.companyAnchors, "contract_numbers"), document_numbers: mergeAnchors(input.companyAnchors, "document_numbers"), document_types: mergeAnchors(input.companyAnchors, "document_types"), counterparty_scope: inferCounterpartyScope(input.userMessage), advance_signal: signals.hasAdvance ? ["advance"] : [], closure_signal: signals.hasClosure ? ["closure"] : [], vat_signal: includeVatAnchors && hasVatSignal ? ["vat"] : [], chain_signal: includeVatAnchors && hasVatSignal ? ["chain"] : [], close_signal: includeMonthCloseAnchors && hasMonthCloseSignal ? ["month_close"] : [], cost_scope: [], rbp_signal: includeRbpAnchors && hasRbpSignal ? ["rbp"] : [], writeoff_signal: includeRbpAnchors && hasRbpSignal ? ["writeoff"] : [], fixed_asset_signal: includeFixedAssetAnchors && hasFixedAssetSignal ? ["fixed_asset"] : [], amortization_signal: includeFixedAssetAnchors && hasFixedAssetSignal ? ["amortization"] : [], expected_fa_set: [], actual_fa_set: [] }; if (includeMonthCloseAnchors && (/(?:^|[^\d])(20|44)(?:[^\d]|$)/.test((resolvedAnchors.account_scope ?? []).join(" ")) || hasMonthCloseSignal)) { resolvedAnchors.cost_scope = ["20_44"]; } // For FA amortization claims, document type is implicit in user intent // even when the phrase does not carry explicit document keywords. if (includeFixedAssetAnchors && hasFixedAssetSignal && (resolvedAnchors.document_types?.length ?? 0) <= 0) { resolvedAnchors.document_types = ["amortization_document"]; } if (input.primaryPeriod) { resolvedAnchors.period = uniqueStrings([...(resolvedAnchors.period ?? []), input.primaryPeriod.from, input.primaryPeriod.to]); } const requiredByClaim = { prove_settlement_closure_state: ["period", "account_scope", "counterparty_scope_or_contract", "closure_signal"], prove_advance_offset_state: ["period", "account_scope", "advance_signal", "settlement_object"], prove_vat_chain_completeness: ["period", "document_types", "vat_signal", "chain_signal"], prove_month_close_state: ["period", "close_signal", "cost_scope"], prove_rbp_tail_state: ["period", "rbp_signal", "writeoff_signal"], prove_fixed_asset_amortization_coverage: [ "period", "fixed_asset_signal", "amortization_signal", "amount_or_document", "account_scope_or_document_type" ] }; const requiredAnchors = requiredByClaim[claimType]; const missingAnchors = missingFromRequired(requiredAnchors, resolvedAnchors); const resolutionRate = requiredAnchors.length > 0 ? Number(((requiredAnchors.length - missingAnchors.length) / requiredAnchors.length).toFixed(4)) : 1; const allowedContextWindow = buildAllowedContextWindow(input.primaryPeriod ?? null); const reasonCodes = []; if (missingAnchors.length > 0) { reasonCodes.push("claim_missing_required_anchors"); } if (resolutionRate < 0.8) { reasonCodes.push("claim_anchor_resolution_low"); } if (!allowedContextWindow && input.primaryPeriod) { reasonCodes.push("controlled_temporal_expansion_window_unavailable"); } const settlementRoleRaw = resolveSettlementRole({ claimType, counterpartyScope: resolvedAnchors.counterparty_scope ?? [], accountPrefixes, userMessage: input.userMessage }); const settlementRole = typeof settlementRoleRaw === "string" ? settlementRoleRaw : undefined; const settlementRoleReason = claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state" ? settlementRole ? [`settlement_role_resolved_${settlementRole}`] : ["no_supplier_customer_anchor"] : []; const polarityResolutionStatus = claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state" ? settlementRole === "supplier" ? "resolved_supplier" : settlementRole === "customer" ? "resolved_customer" : settlementRole === "mixed" ? "mixed" : "unknown" : "not_applicable"; if ((claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state") && (settlementRole === "mixed" || settlementRole === "unknown")) { reasonCodes.push("unresolved_supplier_customer_polarity"); } return { claim_type: claimType, settlement_role: settlementRole, settlement_role_resolution_reason: settlementRoleReason, polarity_resolution_status: polarityResolutionStatus, required_anchors: requiredAnchors, resolved_anchors: resolvedAnchors, missing_anchors: missingAnchors, claim_anchor_resolution_rate: resolutionRate, primary_period: input.primaryPeriod ?? null, allowed_context_window: allowedContextWindow, context_expansion_reasons_allowed: [ "prehistory", "carryover", "post_period_closure", "long_running_contract_context" ], reason_codes: uniqueStrings(reasonCodes) }; } function buildCorpusFromItem(item) { return JSON.stringify({ source_entity: item.source_entity, source_id: item.source_id, period: item.period ?? item.Period, account_context: item.account_context, account_debit: item.account_debit, account_credit: item.account_credit, document_context: item.document_context, relation_pattern_hits: item.relation_pattern_hits, graph_domain_scope: item.graph_domain_scope, lifecycle_markers: item.lifecycle_markers, live_call_id: item.live_call_id, live_call_purpose: item.live_call_purpose, fa_object_hint: item.fa_object_hint, fa_expected_set_candidate: item.fa_expected_set_candidate, fa_actual_set_candidate: item.fa_actual_set_candidate, fa_coverage_status: item.fa_coverage_status }).toLowerCase(); } function buildCorpusFromEvidence(evidence) { return JSON.stringify({ source_ref: evidence.source_ref, pointer: evidence.pointer, payload: evidence.payload, mechanism_note: evidence.mechanism_note, limitation: evidence.limitation }).toLowerCase(); } function requiredChecksByClaim(claimType) { if (claimType === "prove_settlement_closure_state") { return [ "payment_document_found", "contract_matched", "settlement_object_matched", "closing_document_found", "register_closure_entry_found", "posting_link_found" ]; } if (claimType === "prove_advance_offset_state") { return [ "payment_document_found", "advance_marker_found", "settlement_object_matched", "closing_document_found", "register_closure_entry_found", "posting_link_found" ]; } if (claimType === "prove_vat_chain_completeness") { return ["source_document_found", "invoice_found", "tax_register_entry_found", "book_entry_found", "chain_linkage_status"]; } if (claimType === "prove_month_close_state") { return ["close_operation_found", "distribution_step_found", "residual_tail_found"]; } if (claimType === "prove_fixed_asset_amortization_coverage") { return [ "amortization_document_found", "fixed_asset_object_identified", "expected_fa_set_reconstructed", "actual_fa_set_reconstructed", "movement_or_posting_link_found", "missing_fa_candidates_assessed" ]; } return [ "rbp_writeoff_document_found", "rbp_object_identified", "rbp_movement_found", "rbp_period_end_residual_found", "rbp_writeoff_lifecycle_confirmed", "residual_tail_found", "close_contradiction_or_normal_residual" ]; } function detectChecksForCorpus(corpus, claimType, anchors) { const checks = new Set(); const hasContractAnchor = (anchors.contract ?? []).some((token) => token.length >= 3 && corpus.includes(String(token).toLowerCase())) || /(?:contract|договор)/i.test(corpus); const hasSettlementAccount = /(?:\b60(?:\.\d{2})?\b|\b62(?:\.\d{2})?\b|payable|receivable|settlement)/i.test(corpus); const hasPosting = /(?:document_to_posting|posting|проводк)/i.test(corpus); const hasRegister = /(?:register|accumulationregister|accountingregister|регистр)/i.test(corpus); const hasClose = /(?:close|closure|закрыт|reconcile|зач[её]т|tail|хвост)/i.test(corpus); const hasPayment = /(?:payment|оплат|списаниесрасчетногосчета|payment_order|bank_statement)/i.test(corpus); const hasAdvance = /(?:advance|аванс|offset|зач[её]т|62\.02|60\.02)/i.test(corpus); const hasVat = /(?:\bvat\b|ндс|invoice_to_vat|сч[её]т[- ]?фактур|invoice)/i.test(corpus); const hasBook = /(?:книг[аи](?:\s+)?(?:покупок|продаж)|book)/i.test(corpus); const hasChain = /(?:chain|link|document_to_posting|invoice_to_vat|связ)/i.test(corpus); const hasMonthClose = /(?:month[- ]?close|period_close|закрытие\s+месяца|косвен|20|44)/i.test(corpus); const hasDistribution = /(?:distribution|распредел|writeoff|deferred_expense_to_writeoff)/i.test(corpus); const hasRbp = /(?:\brbp\b|рбп|account\s*97|сч[её]т\s*97|deferred)/i.test(corpus); const hasResidual = /(?:tail|остат|незакры|overdue|period_boundary|terminal_state_gap)/i.test(corpus); const hasContradiction = /(?:contradiction|invalid_transition|normal residual|нормальн)/i.test(corpus); const hasRbpWriteoffDoc = /(?:списани[ея]\s+рбп|rbp_writeoff|deferred_expense_document|writeoff document)/i.test(corpus); const hasRbpObject = /(?:rbp[_\s-]?object|объект\s+рбп|analytics|subkonto|расходыбудущихпериодов)/i.test(corpus); const hasMovement = /(?:movement|движен|хозрасчетный|document_to_posting|posting|проводк)/i.test(corpus); const hasPeriodEndResidual = /(?:period_boundary|end_period|2020-07-31|остат)/i.test(corpus); const hasFixedAsset = /(?:fixed_asset|asset_card|объект\s+ос|основн(?:ые|ых)?\s+сред|depreciat|амортиз|account[:\s]*0[12]|\b0[12](?:\.\d{2})?\b)/i.test(corpus); const hasAmortizationDoc = /(?:depreciat|amortization|начислен[а-я]*\s+амортиз|документ\s+амортиз)/i.test(corpus); const hasExpectedFaSet = /(?:expected_fa_set|expected[_\s-]?set|find_fixed_asset_cards_expected_for_period|expected_set_seed|fa_expected_set_candidate)/i.test(corpus); const hasActualFaSet = /(?:actual_fa_set|find_fixed_asset_movements_accounts_01_02|fa_actual_set_candidate|seed_amortization_documents|collect_fa_object_movements)/i.test(corpus); const hasFaCoverageCompare = /(?:expected_vs_actual|compare_expected_vs_actual|missing_fa|coverage_compare|missing_fa_candidates)/i.test(corpus); if (claimType === "prove_settlement_closure_state") { if (hasPayment) checks.add("payment_document_found"); if (hasContractAnchor) checks.add("contract_matched"); if (hasSettlementAccount) checks.add("settlement_object_matched"); if (hasClose) checks.add("closing_document_found"); if (hasRegister) checks.add("register_closure_entry_found"); if (hasPosting) checks.add("posting_link_found"); } else if (claimType === "prove_advance_offset_state") { if (hasPayment) checks.add("payment_document_found"); if (hasAdvance) checks.add("advance_marker_found"); if (hasSettlementAccount) checks.add("settlement_object_matched"); if (hasClose) checks.add("closing_document_found"); if (hasRegister) checks.add("register_closure_entry_found"); if (hasPosting) checks.add("posting_link_found"); } else if (claimType === "prove_vat_chain_completeness") { if (/(?:document|receipt|realization|поступлен|реализац)/i.test(corpus)) checks.add("source_document_found"); if (/(?:invoice|сч[её]т[- ]?фактур)/i.test(corpus)) checks.add("invoice_found"); if (hasRegister || hasVat) checks.add("tax_register_entry_found"); if (hasBook) checks.add("book_entry_found"); if (hasChain) checks.add("chain_linkage_status"); } else if (claimType === "prove_month_close_state") { if (hasMonthClose || hasClose) checks.add("close_operation_found"); if (hasDistribution) checks.add("distribution_step_found"); if (hasResidual) checks.add("residual_tail_found"); } else if (claimType === "prove_fixed_asset_amortization_coverage") { if (hasAmortizationDoc) checks.add("amortization_document_found"); if (hasFixedAsset) checks.add("fixed_asset_object_identified"); if (hasExpectedFaSet) checks.add("expected_fa_set_reconstructed"); if (hasActualFaSet || hasAmortizationDoc) checks.add("actual_fa_set_reconstructed"); if (hasMovement || hasPosting) checks.add("movement_or_posting_link_found"); if (hasFaCoverageCompare || (hasExpectedFaSet && hasActualFaSet)) checks.add("missing_fa_candidates_assessed"); } else { if (hasRbpWriteoffDoc || (hasRbp && hasDistribution)) checks.add("rbp_writeoff_document_found"); if (hasRbpObject || hasRbp) checks.add("rbp_object_identified"); if (hasMovement) checks.add("rbp_movement_found"); if (hasPeriodEndResidual || hasResidual) checks.add("rbp_period_end_residual_found"); if (hasRbp && hasDistribution) checks.add("rbp_writeoff_lifecycle_confirmed"); if (hasResidual) checks.add("residual_tail_found"); if (hasContradiction || hasClose) checks.add("close_contradiction_or_normal_residual"); } return Array.from(checks); } function hasAnchorLink(corpus, claimAudit) { const values = Object.values(claimAudit.resolved_anchors).flat(); return values.some((token) => { const value = String(token ?? "").toLowerCase().trim(); if (value.length < 2) return false; return corpus.includes(value); }); } function resolveContextExpansionDecision(input) { if (!input.period || !input.claimAudit.primary_period) { return { allowed: true, reason: null, inside_primary_period: true }; } const normalized = normalizeDateIso(input.period); if (!normalized) { return { allowed: false, reason: null, inside_primary_period: false }; } const primaryFrom = normalizeDateIso(input.claimAudit.primary_period.from); const primaryTo = normalizeDateIso(input.claimAudit.primary_period.to); if (!primaryFrom || !primaryTo) { return { allowed: true, reason: null, inside_primary_period: true }; } if (normalized >= primaryFrom && normalized <= primaryTo) { return { allowed: true, reason: null, inside_primary_period: true }; } const allowedFrom = normalizeDateIso(input.claimAudit.allowed_context_window?.from ?? ""); const allowedTo = normalizeDateIso(input.claimAudit.allowed_context_window?.to ?? ""); if (allowedFrom && normalized < allowedFrom) { return { allowed: false, reason: null, inside_primary_period: false }; } if (allowedTo && normalized > allowedTo) { return { allowed: false, reason: null, inside_primary_period: false }; } const linked = hasAnchorLink(input.corpus, input.claimAudit) || input.matchedChecks.length > 0; const fromDate = isoToDate(primaryFrom); const toDate = isoToDate(primaryTo); const curDate = isoToDate(normalized); const hasContractAnchor = (input.claimAudit.resolved_anchors.contract?.length ?? 0) > 0; if (!fromDate || !toDate || !curDate) { return { allowed: linked, reason: linked ? "carryover" : null, inside_primary_period: false }; } const diffBefore = Math.floor((fromDate.getTime() - curDate.getTime()) / (24 * 3600 * 1000)); const diffAfter = Math.floor((curDate.getTime() - toDate.getTime()) / (24 * 3600 * 1000)); if (curDate < fromDate) { if (linked && hasContractAnchor && diffBefore > 31) { return { allowed: true, reason: "long_running_contract_context", inside_primary_period: false }; } if (linked) { return { allowed: true, reason: "prehistory", inside_primary_period: false }; } if (diffBefore <= 31) { return { allowed: true, reason: "carryover", inside_primary_period: false }; } return { allowed: false, reason: null, inside_primary_period: false }; } if (curDate > toDate) { if (diffAfter <= 31) { return { allowed: true, reason: "carryover", inside_primary_period: false }; } if (linked && hasContractAnchor) { return { allowed: true, reason: "long_running_contract_context", inside_primary_period: false }; } if (linked) { return { allowed: true, reason: "post_period_closure", inside_primary_period: false }; } return { allowed: false, reason: null, inside_primary_period: false }; } return { allowed: true, reason: null, inside_primary_period: true }; } function evidenceSourceNamespaceFromItem(item) { const sourceLayer = String(item.source_layer ?? "").toLowerCase(); if (sourceLayer.includes("snapshot")) { return "snapshot_2020"; } return "assistant_derived"; } function buildDerivedEvidenceFromItem(input) { const sourceEntity = String(input.item.source_entity ?? "unknown"); const sourceId = String(input.item.source_id ?? `derived-${(0, nanoid_1.nanoid)(8)}`); const period = String(input.item.period ?? input.item.Period ?? "").trim() || null; const namespace = evidenceSourceNamespaceFromItem(input.item); const canonical = `evidence_source_ref_v1|${namespace}|${sourceEntity.toLowerCase()}|${sourceId.toLowerCase()}|${String(period ?? "").toLowerCase()}`; const confidence = input.matchedChecks.length >= 2 ? "high" : "medium"; return { evidence_id: `claim-ev-${(0, nanoid_1.nanoid)(10)}`, claim_ref: `claim:${input.claimType}`, source_type: "derived", source_ref: { schema_version: "evidence_source_ref_v1", namespace, entity: sourceEntity, id: sourceId, period, canonical_ref: canonical }, pointer: { fragment_id: input.result.fragment_id, route: input.result.route, source: { namespace, entity: sourceEntity, id: sourceId, period }, locator: { field_path: null, item_index: null } }, evidence_kind: "mechanism_link", mechanism_note: input.matchedChecks[0] ?? null, confidence, limitation: null, payload: { from_targeted_item: true, claim_type: input.claimType, claim_target_checks: input.matchedChecks, context_expansion_allowed: input.expansion.allowed, context_expansion_reason: input.expansion.reason, period, source_entity: sourceEntity, source_id: sourceId, account_context: Array.isArray(input.item.account_context) ? input.item.account_context : [], account_debit: input.item.account_debit ?? null, account_credit: input.item.account_credit ?? null, relation_pattern_hits: Array.isArray(input.item.relation_pattern_hits) ? input.item.relation_pattern_hits : [], fa_object_hint: String(input.item.fa_object_hint ?? "").trim() || null, fa_expected_set_candidate: Boolean(input.item.fa_expected_set_candidate), fa_actual_set_candidate: Boolean(input.item.fa_actual_set_candidate), fa_coverage_status: String(input.item.fa_coverage_status ?? "").trim() || null } }; } function buildClaimStatusTemplate(requiredChecks) { const out = {}; for (const check of requiredChecks) { out[check] = "not_found"; } return out; } function normalizeFaObjectToken(value) { const normalized = String(value ?? "") .replace(/\s+/g, " ") .trim(); if (!normalized) { return null; } if (/^live movement row #\d+$/i.test(normalized)) { return null; } return normalized.slice(0, 140); } function periodFromEvidence(evidence) { const payload = toObject(evidence.payload); return (String(evidence.source_ref?.period ?? "").trim() || String(evidence.pointer?.source?.period ?? "").trim() || String(payload?.period ?? "").trim() || null); } function collectFaCoverage(input) { const state = new Map(); const touch = (objectName) => { const key = objectName.toLowerCase(); const existing = state.get(key); if (existing) { return existing; } const created = { expected: false, actual: false, movement: false, posting: false, docs: new Set(), periods: new Set() }; state.set(key, created); return created; }; for (const result of input.retrievalResults) { const items = Array.isArray(result.items) ? result.items : []; for (const item of items) { const objectToken = normalizeFaObjectToken(String(item.fa_object_hint ?? item.display_name ?? item.source_id ?? "").trim()); if (!objectToken) { continue; } const slot = touch(objectToken); if (Boolean(item.fa_expected_set_candidate)) { slot.expected = true; } if (Boolean(item.fa_actual_set_candidate)) { slot.actual = true; } const corpus = JSON.stringify(item).toLowerCase(); if (/(?:movement|движен|хозрасчет|document_to_posting)/i.test(corpus)) { slot.movement = true; } if (/(?:posting|проводк|account_)/i.test(corpus)) { slot.posting = true; } const documentContext = Array.isArray(item.document_context) ? item.document_context : []; for (const doc of documentContext) { const token = String(doc ?? "").trim(); if (token) { slot.docs.add(token); } } const period = String(item.period ?? item.Period ?? "").trim(); if (period) { slot.periods.add(period); } } const evidence = Array.isArray(result.evidence) ? result.evidence : []; for (const evidenceItem of evidence) { const payload = toObject(evidenceItem.payload) ?? {}; const objectToken = normalizeFaObjectToken(String(payload.fa_object_hint ?? evidenceItem.source_ref?.id ?? evidenceItem.pointer?.source?.id ?? "").trim()); if (!objectToken) { continue; } const slot = touch(objectToken); if (Boolean(payload.fa_expected_set_candidate)) { slot.expected = true; } if (Boolean(payload.fa_actual_set_candidate)) { slot.actual = true; } const corpus = JSON.stringify({ payload, mechanism_note: evidenceItem.mechanism_note, source_ref: evidenceItem.source_ref }).toLowerCase(); if (/(?:movement|движен|хозрасчет|document_to_posting)/i.test(corpus)) { slot.movement = true; } if (/(?:posting|проводк|account_)/i.test(corpus)) { slot.posting = true; } const documentContext = Array.isArray(payload.document_context) ? payload.document_context : []; for (const doc of documentContext) { const token = String(doc ?? "").trim(); if (token) { slot.docs.add(token); } } const period = periodFromEvidence(evidenceItem); if (period) { slot.periods.add(period); } } } const entries = Array.from(state.entries()); const expectedSet = entries .filter(([, slot]) => slot.expected) .map(([objectName]) => objectName) .slice(0, 32); const actualSet = entries .filter(([, slot]) => slot.actual) .map(([objectName]) => objectName) .slice(0, 32); const expectedResolved = expectedSet.length > 0 ? expectedSet : actualSet; const missingCandidates = expectedResolved.filter((item) => !actualSet.includes(item)).slice(0, 32); const uncertainCandidates = entries .filter(([, slot]) => !slot.expected && !slot.actual) .map(([objectName]) => objectName) .slice(0, 32); const relationMap = entries.slice(0, 48).map(([objectName, slot]) => { const coverageStatus = slot.expected && slot.actual ? "covered" : slot.expected && !slot.actual ? "missing" : "uncertain"; return { fa_object: objectName, document_amortization: Array.from(slot.docs).slice(0, 4), movement: slot.movement, posting: slot.posting, period: Array.from(slot.periods).slice(0, 4), coverage_status: coverageStatus }; }); return { expectedSet: expectedResolved, actualSet, missingCandidates, uncertainCandidates, relationMap }; } function applyTargetedEvidenceAcquisition(input) { const requiredChecks = requiredChecksByClaim(input.claimAudit.claim_type); const checkStatus = buildClaimStatusTemplate(requiredChecks); let targetedItemHits = 0; let targetedEvidenceHits = 0; const sourceRefs = new Set(); const adjustedResults = input.retrievalResults.map((result) => { const items = Array.isArray(result.items) ? result.items : []; const targetedItems = []; const derivedEvidence = []; for (const item of items) { const corpus = buildCorpusFromItem(item); const matchedChecks = detectChecksForCorpus(corpus, input.claimAudit.claim_type, input.claimAudit.resolved_anchors); for (const check of matchedChecks) { if (check in checkStatus) checkStatus[check] = "found"; } if (matchedChecks.length <= 0) { continue; } targetedItemHits += 1; const expansion = resolveContextExpansionDecision({ period: String(item.period ?? item.Period ?? "").trim() || null, claimAudit: input.claimAudit, corpus, matchedChecks }); const enrichedItem = { ...item, claim_target_checks: matchedChecks, context_expansion_allowed: expansion.allowed, context_expansion_reason: expansion.reason }; targetedItems.push(enrichedItem); if (derivedEvidence.length < 8) { const evidence = buildDerivedEvidenceFromItem({ result, item: enrichedItem, claimType: input.claimAudit.claim_type, matchedChecks, expansion }); derivedEvidence.push(evidence); sourceRefs.add(evidence.source_ref.canonical_ref); } } const evidence = Array.isArray(result.evidence) ? result.evidence : []; const targetedEvidence = []; for (const evidenceItem of evidence) { const corpus = buildCorpusFromEvidence(evidenceItem); const matchedChecks = detectChecksForCorpus(corpus, input.claimAudit.claim_type, input.claimAudit.resolved_anchors); for (const check of matchedChecks) { if (check in checkStatus) checkStatus[check] = "found"; } if (matchedChecks.length <= 0) { continue; } const payload = toObject(evidenceItem.payload) ?? {}; const expansion = resolveContextExpansionDecision({ period: String(evidenceItem.source_ref?.period ?? "").trim() || String(evidenceItem.pointer?.source?.period ?? "").trim() || String(payload.period ?? "").trim() || null, claimAudit: input.claimAudit, corpus, matchedChecks }); targetedEvidence.push({ ...evidenceItem, payload: { ...payload, claim_type: input.claimAudit.claim_type, claim_target_checks: matchedChecks, context_expansion_allowed: expansion.allowed, context_expansion_reason: expansion.reason } }); } const mergedEvidence = [...targetedEvidence, ...derivedEvidence]; targetedEvidenceHits += mergedEvidence.length; for (const item of mergedEvidence) { sourceRefs.add(item.source_ref.canonical_ref); } const summary = { ...(toObject(result.summary) ?? {}), claim_bound_targeting: { claim_type: input.claimAudit.claim_type, required_checks: requiredChecks, targeted_items: targetedItems.length, targeted_evidence: mergedEvidence.length, derived_evidence_added: derivedEvidence.length } }; return { ...result, items: targetedItems.length > 0 ? targetedItems : items, evidence: mergedEvidence.length > 0 ? mergedEvidence : evidence, summary }; }); const foundChecks = Object.values(checkStatus).filter((status) => status === "found").length; const targetedEvidenceHitRate = requiredChecks.length > 0 ? Number((foundChecks / requiredChecks.length).toFixed(4)) : 0; const reasonCodes = []; if (targetedEvidenceHits <= 0) { reasonCodes.push("targeted_evidence_not_found"); } if (targetedEvidenceHitRate < 0.8) { reasonCodes.push("targeted_evidence_hit_rate_low"); } const faCoverage = input.claimAudit.claim_type === "prove_fixed_asset_amortization_coverage" ? collectFaCoverage({ retrievalResults: adjustedResults }) : null; if (faCoverage) { if (faCoverage.expectedSet.length <= 0) { reasonCodes.push("fa_expected_set_not_reconstructed"); } if (faCoverage.actualSet.length <= 0) { reasonCodes.push("fa_actual_set_not_reconstructed"); } } return { retrievalResults: adjustedResults, audit: { claim_type: input.claimAudit.claim_type, required_checks: requiredChecks, check_status: checkStatus, targeted_item_hits: targetedItemHits, targeted_evidence_hits: targetedEvidenceHits, targeted_evidence_hit_rate: targetedEvidenceHitRate, targeted_evidence_source_refs: Array.from(sourceRefs).slice(0, 24), ...(faCoverage ? { fa_expected_set: faCoverage.expectedSet, fa_actual_set_from_amortization: faCoverage.actualSet, fa_missing_candidates: faCoverage.missingCandidates, fa_uncertain_candidates: faCoverage.uncertainCandidates, fa_relation_map: faCoverage.relationMap } : {}), reason_codes: uniqueStrings(reasonCodes) } }; }