"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = void 0; exports.executeAssistantMcpDiscoveryPilot = executeAssistantMcpDiscoveryPilot; const addressMcpClient_1 = require("./addressMcpClient"); const assistantMcpDiscoveryRuntimeAdapter_1 = require("./assistantMcpDiscoveryRuntimeAdapter"); const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy"); const addressRecipeCatalog_1 = require("./addressRecipeCatalog"); exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = "assistant_mcp_discovery_pilot_executor_v1"; const DEFAULT_DEPS = { executeAddressMcpQuery: addressMcpClient_1.executeAddressMcpQuery }; function toNonEmptyString(value) { if (value === null || value === undefined) { return null; } const text = String(value).trim(); return text.length > 0 ? text : null; } function normalizeReasonCode(value) { const normalized = value .trim() .replace(/[^\p{L}\p{N}_.:-]+/gu, "_") .replace(/^_+|_+$/g, "") .toLowerCase(); return normalized.length > 0 ? normalized.slice(0, 120) : null; } function pushReason(target, value) { const normalized = normalizeReasonCode(value); if (normalized && !target.includes(normalized)) { target.push(normalized); } } function pushUnique(target, value) { const text = value.trim(); if (text && !target.includes(text)) { target.push(text); } } function firstEntityCandidate(planner) { const candidates = planner.discovery_plan.turn_meaning_ref?.explicit_entity_candidates ?? []; for (const candidate of candidates) { const text = toNonEmptyString(candidate); if (text) { return text; } } return null; } function dateScopeToFilters(dateScope) { if (!dateScope) { return {}; } const yearMatch = dateScope.match(/^(\d{4})$/); if (yearMatch) { return { period_from: `${yearMatch[1]}-01-01`, period_to: `${yearMatch[1]}-12-31` }; } const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/); if (dateMatch) { const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`; return { period_from: date, period_to: date }; } return {}; } function buildLifecycleFilters(planner) { const meaning = planner.discovery_plan.turn_meaning_ref; const counterparty = firstEntityCandidate(planner); const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); return { ...dateScopeToFilters(dateScope), ...(counterparty ? { counterparty } : {}), ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; } function buildValueFlowFilters(planner) { const meaning = planner.discovery_plan.turn_meaning_ref; const counterparty = firstEntityCandidate(planner); const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); return { ...dateScopeToFilters(dateScope), ...(counterparty ? { counterparty } : {}), ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; } function isLifecyclePilotEligible(planner) { const meaning = planner.discovery_plan.turn_meaning_ref; const domain = String(meaning?.asked_domain_family ?? "").toLowerCase(); const action = String(meaning?.asked_action_family ?? "").toLowerCase(); const combined = `${domain} ${action}`; return (planner.proposed_primitives.includes("query_documents") && (combined.includes("lifecycle") || combined.includes("activity") || combined.includes("duration") || combined.includes("age"))); } function isValueFlowPilotEligible(planner) { const meaning = planner.discovery_plan.turn_meaning_ref; const domain = String(meaning?.asked_domain_family ?? "").toLowerCase(); const action = String(meaning?.asked_action_family ?? "").toLowerCase(); const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase(); const combined = `${domain} ${action} ${unsupported}`; return (planner.proposed_primitives.includes("query_movements") && (combined.includes("turnover") || combined.includes("revenue") || combined.includes("payment") || combined.includes("payout") || combined.includes("value"))); } function skippedProbeResult(step, limitation) { return { primitive_id: step.primitive_id, status: "skipped", rows_received: 0, rows_matched: 0, limitation }; } function queryResultToProbeResult(primitiveId, result) { return { primitive_id: primitiveId, status: result.error ? "error" : "ok", rows_received: result.fetched_rows, rows_matched: result.matched_rows, limitation: result.error }; } function summarizeLifecycleRows(result) { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP document rows fetched"; } return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched lifecycle scope`; } function summarizeValueFlowRows(result) { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP value-flow rows fetched"; } return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`; } function rowDateValue(row) { const candidates = [ row["Период"], row["Period"], row["period"], row["Дата"], row["Date"], row["date"] ]; for (const candidate of candidates) { const text = toNonEmptyString(candidate); const match = text?.match(/(\d{4})-(\d{2})-(\d{2})/); if (match) { return `${match[1]}-${match[2]}-${match[3]}`; } } return null; } function rowAmountValue(row) { const candidates = [ row["Сумма"], row["РЎСѓРјРјР°"], row["СуммаДокумента"], row["СуммаДокумента"], row["Amount"], row["amount"], row["Total"], row["total"] ]; for (const candidate of candidates) { if (typeof candidate === "number" && Number.isFinite(candidate)) { return candidate; } const text = toNonEmptyString(candidate); if (!text) { continue; } const normalized = text .replace(/\s+/g, "") .replace(/\u00a0/g, "") .replace(",", ".") .replace(/[^\d.-]/g, ""); const parsed = Number(normalized); if (Number.isFinite(parsed)) { return parsed; } } return null; } function monthDiff(firstIsoDate, latestIsoDate) { const first = new Date(`${firstIsoDate}T00:00:00.000Z`); const latest = new Date(`${latestIsoDate}T00:00:00.000Z`); if (Number.isNaN(first.getTime()) || Number.isNaN(latest.getTime()) || latest < first) { return 0; } let months = (latest.getUTCFullYear() - first.getUTCFullYear()) * 12; months += latest.getUTCMonth() - first.getUTCMonth(); if (latest.getUTCDate() < first.getUTCDate()) { months -= 1; } return Math.max(0, months); } function formatDurationHumanRu(totalMonths) { const years = Math.floor(totalMonths / 12); const months = totalMonths % 12; const parts = []; if (years > 0) { parts.push(`${years} ${years === 1 ? "год" : years >= 2 && years <= 4 ? "года" : "лет"}`); } if (months > 0) { parts.push(`${months} ${months === 1 ? "месяц" : months >= 2 && months <= 4 ? "месяца" : "месяцев"}`); } return parts.length > 0 ? parts.join(" ") : "меньше месяца"; } function deriveActivityPeriod(result) { if (!result || result.error || result.matched_rows <= 0) { return null; } const dates = result.rows .map((row) => rowDateValue(row)) .filter((value) => Boolean(value)) .sort(); if (dates.length === 0) { return null; } const first = dates[0]; const latest = dates[dates.length - 1]; const totalMonths = monthDiff(first, latest); return { first_activity_date: first, latest_activity_date: latest, matched_rows: result.matched_rows, duration_total_months: totalMonths, duration_years: Math.floor(totalMonths / 12), duration_months_remainder: totalMonths % 12, duration_human_ru: formatDurationHumanRu(totalMonths), inference_basis: "first_and_latest_confirmed_1c_activity_rows" }; } function formatAmountHumanRu(amount) { const formatted = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 2, minimumFractionDigits: Number.isInteger(amount) ? 0 : 2 }) .format(amount) .replace(/\u00a0/g, " "); return `${formatted} руб.`; } function deriveValueFlow(result, counterparty, periodScope) { if (!result || result.error || result.matched_rows <= 0) { return null; } let totalAmount = 0; let rowsWithAmount = 0; for (const row of result.rows) { const amount = rowAmountValue(row); if (amount !== null) { totalAmount += amount; rowsWithAmount += 1; } } if (rowsWithAmount <= 0) { return null; } const dates = result.rows .map((row) => rowDateValue(row)) .filter((value) => Boolean(value)) .sort(); return { counterparty, period_scope: periodScope, rows_matched: result.matched_rows, rows_with_amount: rowsWithAmount, total_amount: totalAmount, total_amount_human_ru: formatAmountHumanRu(totalAmount), first_movement_date: dates[0] ?? null, latest_movement_date: dates[dates.length - 1] ?? null, inference_basis: "sum_of_confirmed_1c_value_flow_rows" }; } function buildLifecycleConfirmedFacts(result, counterparty) { if (result.error || result.matched_rows <= 0) { return []; } return [ counterparty ? `1C activity rows were found for counterparty ${counterparty}` : "1C activity rows were found for the requested counterparty scope" ]; } function buildValueFlowConfirmedFacts(result, counterparty) { if (result.error || result.matched_rows <= 0) { return []; } return [ counterparty ? `1C value-flow rows were found for counterparty ${counterparty}` : "1C value-flow rows were found for the requested counterparty scope" ]; } function buildLifecycleInferredFacts(result) { if (result.error || result.fetched_rows <= 0) { return []; } return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"]; } function buildValueFlowInferredFacts(derived) { if (!derived) { return []; } return ["Counterparty value-flow total was calculated from confirmed 1C movement rows"]; } function buildLifecycleUnknownFacts() { return ["Legal registration date is not proven by this MCP discovery pilot"]; } function buildValueFlowUnknownFacts(periodScope) { return [ periodScope ? "Full turnover outside the checked period is not proven by this MCP discovery pilot" : "Full all-time turnover is not proven without an explicit checked period" ]; } function buildEmptyEvidence(planner, dryRun, probeResults, reason) { return (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({ plan: planner.discovery_plan, probeResults, unknownFacts: [reason], queryLimitations: [reason], recommendedNextProbe: dryRun.user_facing_fallback }); } async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) { const dryRun = (0, assistantMcpDiscoveryRuntimeAdapter_1.buildAssistantMcpDiscoveryRuntimeDryRun)(planner); const reasonCodes = [...dryRun.reason_codes]; const executedPrimitives = []; const skippedPrimitives = []; const probeResults = []; const queryLimitations = []; if (dryRun.adapter_status === "blocked") { pushReason(reasonCodes, "pilot_blocked_before_mcp_execution"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot was blocked before execution"); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "blocked", pilot_scope: "counterparty_lifecycle_query_documents_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_activity_period: null, derived_value_flow: null, query_limitations: ["MCP discovery pilot was blocked before execution"], reason_codes: reasonCodes }; } if (dryRun.adapter_status !== "dry_run_ready") { pushReason(reasonCodes, "pilot_needs_clarification_before_mcp_execution"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot needs more scope before execution"); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "skipped_needs_clarification", pilot_scope: "counterparty_lifecycle_query_documents_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_activity_period: null, derived_value_flow: null, query_limitations: ["MCP discovery pilot needs more scope before execution"], reason_codes: reasonCodes }; } const lifecyclePilotEligible = isLifecyclePilotEligible(planner); const valueFlowPilotEligible = isValueFlowPilotEligible(planner); if (!lifecyclePilotEligible && !valueFlowPilotEligible) { pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution"); for (const step of dryRun.execution_steps) { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_scope_unsupported_for_live_execution")); } const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot scope is not implemented yet"); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "counterparty_lifecycle_query_documents_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_activity_period: null, derived_value_flow: null, query_limitations: ["MCP discovery pilot scope is not implemented yet"], reason_codes: reasonCodes }; } const counterparty = firstEntityCandidate(planner); const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope); if (valueFlowPilotEligible) { let queryResult = null; const filters = buildValueFlowFilters(planner); const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("customer_revenue_and_payments", filters); if (!selection.selected_recipe) { pushReason(reasonCodes, "pilot_value_flow_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Value-flow recipe is not available"); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "counterparty_value_flow_query_movements_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_activity_period: null, derived_value_flow: null, query_limitations: ["Value-flow recipe is not available"], reason_codes: reasonCodes }; } const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters); for (const step of dryRun.execution_steps) { if (step.primitive_id !== "query_movements") { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_value_flow_uses_query_movements_and_derives_aggregate")); continue; } queryResult = await deps.executeAddressMcpQuery({ query: recipePlan.query, limit: recipePlan.limit, account_scope: recipePlan.account_scope }); executedPrimitives.push(step.primitive_id); probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult)); if (queryResult.error) { pushUnique(queryLimitations, queryResult.error); pushReason(reasonCodes, "pilot_query_movements_mcp_error"); } else { pushReason(reasonCodes, "pilot_query_movements_mcp_executed"); } } const sourceRowsSummary = queryResult ? summarizeValueFlowRows(queryResult) : null; const derivedValueFlow = deriveValueFlow(queryResult, counterparty, dateScope); if (derivedValueFlow) { pushReason(reasonCodes, "pilot_derived_value_flow_from_confirmed_rows"); } const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildValueFlowConfirmedFacts(queryResult, counterparty) : [], inferredFacts: buildValueFlowInferredFacts(derivedValueFlow), unknownFacts: buildValueFlowUnknownFacts(dateScope), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "counterparty_value_flow_query_movements_v1", dry_run: dryRun, mcp_execution_performed: executedPrimitives.length > 0, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: sourceRowsSummary, derived_activity_period: null, derived_value_flow: derivedValueFlow, query_limitations: queryLimitations, reason_codes: reasonCodes }; } let queryResult = null; const filters = buildLifecycleFilters(planner); const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("counterparty_activity_lifecycle", filters); if (!selection.selected_recipe) { pushReason(reasonCodes, "pilot_lifecycle_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Lifecycle recipe is not available"); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "counterparty_lifecycle_query_documents_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_activity_period: null, derived_value_flow: null, query_limitations: ["Lifecycle recipe is not available"], reason_codes: reasonCodes }; } const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters); for (const step of dryRun.execution_steps) { if (step.primitive_id !== "query_documents") { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents")); continue; } queryResult = await deps.executeAddressMcpQuery({ query: recipePlan.query, limit: recipePlan.limit, account_scope: recipePlan.account_scope }); executedPrimitives.push(step.primitive_id); probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult)); if (queryResult.error) { pushUnique(queryLimitations, queryResult.error); pushReason(reasonCodes, "pilot_query_documents_mcp_error"); } else { pushReason(reasonCodes, "pilot_query_documents_mcp_executed"); } } const sourceRowsSummary = queryResult ? summarizeLifecycleRows(queryResult) : null; const derivedActivityPeriod = deriveActivityPeriod(queryResult); if (derivedActivityPeriod) { pushReason(reasonCodes, "pilot_derived_activity_period_from_confirmed_rows"); } const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildLifecycleConfirmedFacts(queryResult, counterparty) : [], inferredFacts: queryResult ? buildLifecycleInferredFacts(queryResult) : [], unknownFacts: buildLifecycleUnknownFacts(), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "counterparty_lifecycle_query_documents_v1", dry_run: dryRun, mcp_execution_performed: executedPrimitives.length > 0, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: sourceRowsSummary, derived_activity_period: derivedActivityPeriod, derived_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; }