import { executeAddressMcpMetadata, executeAddressMcpQuery, type AddressMcpMetadataRowsResult } from "./addressMcpClient"; import { buildAssistantMcpDiscoveryRuntimeDryRun, type AssistantMcpDiscoveryRuntimeDryRunContract, type AssistantMcpDiscoveryRuntimeStepContract } from "./assistantMcpDiscoveryRuntimeAdapter"; import type { AssistantMcpDiscoveryChainId, AssistantMcpDiscoveryPlannerContract } from "./assistantMcpDiscoveryPlanner"; import { resolveAssistantMcpDiscoveryEvidence, type AssistantMcpDiscoveryEvidenceContract, type AssistantMcpDiscoveryProbeResult } from "./assistantMcpDiscoveryPolicy"; import { buildAddressRecipePlan, selectAddressRecipe } from "./addressRecipeCatalog"; import type { AddressFilterSet, AddressIntent } from "../types/addressQuery"; export const ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = "assistant_mcp_discovery_pilot_executor_v1" as const; export type AssistantMcpDiscoveryPilotStatus = | "executed" | "skipped_needs_clarification" | "blocked" | "unsupported"; export interface AssistantMcpDiscoveryPilotExecutorDeps { executeAddressMcpQuery?: typeof executeAddressMcpQuery; executeAddressMcpMetadata?: typeof executeAddressMcpMetadata; } interface ResolvedAssistantMcpDiscoveryPilotExecutorDeps { executeAddressMcpQuery: typeof executeAddressMcpQuery; executeAddressMcpMetadata: typeof executeAddressMcpMetadata; } export interface AssistantMcpDiscoveryDerivedActivityPeriod { first_activity_date: string; latest_activity_date: string; matched_rows: number; duration_total_months: number; duration_years: number; duration_months_remainder: number; duration_human_ru: string; inference_basis: "first_and_latest_confirmed_1c_activity_rows"; } export type AssistantMcpDiscoveryAggregationAxis = "month"; export type AssistantMcpDiscoveryNetDirection = "net_incoming" | "net_outgoing" | "balanced"; export interface AssistantMcpDiscoveryValueFlowMonthBucket { month_bucket: string; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; } export interface AssistantMcpDiscoveryDerivedValueFlow { value_flow_direction: "incoming_customer_revenue" | "outgoing_supplier_payout"; counterparty: string | null; period_scope: string | null; aggregation_axis: AssistantMcpDiscoveryAggregationAxis | null; rows_matched: number; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; first_movement_date: string | null; latest_movement_date: string | null; coverage_limited_by_probe_limit: boolean; coverage_recovered_by_period_chunking: boolean; period_chunking_granularity: AssistantMcpDiscoveryAggregationAxis | null; monthly_breakdown: AssistantMcpDiscoveryValueFlowMonthBucket[]; inference_basis: "sum_of_confirmed_1c_value_flow_rows"; } export interface AssistantMcpDiscoveryRankedValueFlowBucket { axis_value: string; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; } export interface AssistantMcpDiscoveryDerivedRankedValueFlow { value_flow_direction: "incoming_customer_revenue" | "outgoing_supplier_payout"; ranking_need: "top_desc" | "bottom_asc"; ranking_axis: "counterparty"; organization_scope: string | null; period_scope: string | null; rows_matched: number; rows_with_amount: number; ranked_values: AssistantMcpDiscoveryRankedValueFlowBucket[]; coverage_limited_by_probe_limit: boolean; coverage_recovered_by_period_chunking: boolean; period_chunking_granularity: AssistantMcpDiscoveryAggregationAxis | null; inference_basis: "ranked_counterparty_totals_from_confirmed_1c_value_flow_rows"; } export interface AssistantMcpDiscoveryValueFlowSideSummary { rows_matched: number; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; first_movement_date: string | null; latest_movement_date: string | null; coverage_limited_by_probe_limit: boolean; coverage_recovered_by_period_chunking: boolean; period_chunking_granularity: AssistantMcpDiscoveryAggregationAxis | null; } export interface AssistantMcpDiscoveryBidirectionalValueFlowMonthBucket { month_bucket: string; incoming_total_amount: number; incoming_total_amount_human_ru: string; incoming_rows_with_amount: number; outgoing_total_amount: number; outgoing_total_amount_human_ru: string; outgoing_rows_with_amount: number; net_amount: number; net_amount_human_ru: string; net_direction: AssistantMcpDiscoveryNetDirection; } export interface AssistantMcpDiscoveryBusinessOverviewYearBucket { year_bucket: string; incoming_total_amount: number; incoming_total_amount_human_ru: string; incoming_rows_with_amount: number; outgoing_total_amount: number; outgoing_total_amount_human_ru: string; outgoing_rows_with_amount: number; net_amount: number; net_amount_human_ru: string; net_direction: AssistantMcpDiscoveryNetDirection; } export interface AssistantMcpDiscoveryDerivedBidirectionalValueFlow { counterparty: string | null; period_scope: string | null; aggregation_axis: AssistantMcpDiscoveryAggregationAxis | null; incoming_customer_revenue: AssistantMcpDiscoveryValueFlowSideSummary; outgoing_supplier_payout: AssistantMcpDiscoveryValueFlowSideSummary; net_amount: number; net_amount_human_ru: string; net_direction: AssistantMcpDiscoveryNetDirection; coverage_limited_by_probe_limit: boolean; coverage_recovered_by_period_chunking: boolean; period_chunking_granularity: AssistantMcpDiscoveryAggregationAxis | null; monthly_breakdown: AssistantMcpDiscoveryBidirectionalValueFlowMonthBucket[]; inference_basis: "incoming_minus_outgoing_confirmed_1c_value_flow_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverview { organization_scope: string | null; period_scope: string | null; incoming_customer_revenue: AssistantMcpDiscoveryValueFlowSideSummary; outgoing_supplier_payout: AssistantMcpDiscoveryValueFlowSideSummary; net_amount: number; net_amount_human_ru: string; net_direction: AssistantMcpDiscoveryNetDirection; top_customers: AssistantMcpDiscoveryRankedValueFlowBucket[]; top_suppliers: AssistantMcpDiscoveryRankedValueFlowBucket[]; yearly_breakdown: AssistantMcpDiscoveryBusinessOverviewYearBucket[]; activity_period: AssistantMcpDiscoveryDerivedActivityPeriod | null; tax_position: AssistantMcpDiscoveryDerivedBusinessOverviewTaxPosition | null; trading_margin_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewTradingMarginProxy | null; debt_position: AssistantMcpDiscoveryDerivedBusinessOverviewDebtPosition | null; debt_open_settlement_quality: AssistantMcpDiscoveryDerivedBusinessOverviewDebtOpenSettlementQuality | null; debt_staleness_risk_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy | null; inventory_position: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; inventory_turnover_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null; inventory_staleness_risk_proxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null; coverage_limited_by_probe_limit: boolean; checked_signal_count: number; missing_signal_families: string[]; inference_basis: | "business_overview_from_confirmed_1c_money_and_activity_rows" | "business_overview_from_confirmed_1c_money_activity_and_tax_rows" | "business_overview_from_confirmed_1c_money_activity_and_debt_rows" | "business_overview_from_confirmed_1c_money_activity_tax_and_debt_rows" | "business_overview_from_confirmed_1c_multi_family_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewTaxPosition { period_scope: string; rows_matched: number; rows_with_amount: number; sales_vat_amount: number; sales_vat_amount_human_ru: string; purchase_vat_amount: number; purchase_vat_amount_human_ru: string; net_vat_amount: number; net_vat_amount_human_ru: string; net_vat_direction: "vat_to_pay" | "vat_to_recover_or_offset" | "balanced"; inference_basis: "sales_book_minus_purchase_book_confirmed_1c_vat_rows"; } export interface AssistantMcpDiscoveryBusinessOverviewTradingItemBucket { item: string; sales_revenue: number; sales_revenue_human_ru: string; purchase_cost_proxy: number; purchase_cost_proxy_human_ru: string; gross_spread_proxy: number; gross_spread_proxy_human_ru: string; sales_quantity: number; purchase_quantity: number; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewTradingMarginProxy { period_scope: string; rows_matched: number; sales_rows_with_amount: number; purchase_rows_with_amount: number; sales_revenue: number; sales_revenue_human_ru: string; purchase_cost_proxy: number; purchase_cost_proxy_human_ru: string; gross_spread_proxy: number; gross_spread_proxy_human_ru: string; margin_to_revenue_pct: number | null; markup_to_purchase_pct: number | null; top_items_by_sales: AssistantMcpDiscoveryBusinessOverviewTradingItemBucket[]; inference_basis: "sales_documents_minus_purchase_documents_confirmed_1c_rows"; } export interface AssistantMcpDiscoveryBusinessOverviewDebtSideSummary { rows_matched: number; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; top_counterparties: AssistantMcpDiscoveryRankedValueFlowBucket[]; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewDebtPosition { as_of_date: string; receivables: AssistantMcpDiscoveryBusinessOverviewDebtSideSummary; payables: AssistantMcpDiscoveryBusinessOverviewDebtSideSummary; net_debt_position_amount: number; net_debt_position_amount_human_ru: string; net_debt_position_direction: "net_receivable" | "net_payable" | "balanced"; inference_basis: "receivables_minus_payables_confirmed_1c_balance_rows"; } export interface AssistantMcpDiscoveryBusinessOverviewDebtOpenContractBucket { contract: string; counterparty: string | null; contract_start_date: string | null; rows_with_amount: number; total_amount: number; total_amount_human_ru: string; share_of_gross_open_amount_pct: number | null; } export interface AssistantMcpDiscoveryBusinessOverviewDebtAgeContractBucket { contract: string; counterparty: string | null; start_date: string; age_days: number | null; total_amount: number; total_amount_human_ru: string; share_of_gross_open_amount_pct: number | null; } export interface AssistantMcpDiscoveryBusinessOverviewDebtAgeSignal { contracts_with_start_date: number; oldest_start_date: string | null; latest_start_date: string | null; max_age_days: number | null; top_aged_contracts: AssistantMcpDiscoveryBusinessOverviewDebtAgeContractBucket[]; inference_basis: "contract_dates_from_open_settlement_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewDebtOpenSettlementQuality { as_of_date: string; rows_matched: number; rows_with_amount: number; gross_open_amount: number; gross_open_amount_human_ru: string; unique_counterparties: number; unique_contracts: number; rows_without_counterparty: number; rows_without_contract: number; top_counterparties: AssistantMcpDiscoveryRankedValueFlowBucket[]; top_contracts: AssistantMcpDiscoveryBusinessOverviewDebtOpenContractBucket[]; concentration_top_counterparty_pct: number | null; concentration_top_contract_pct: number | null; age_signal: AssistantMcpDiscoveryBusinessOverviewDebtAgeSignal | null; inference_basis: "open_contracts_confirmed_1c_balance_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy { as_of_date: string; gross_open_amount: number; gross_open_amount_human_ru: string; oldest_contract_start_date: string; max_contract_age_days: number; top_contract: string; top_contract_counterparty: string | null; top_contract_amount: number; top_contract_amount_human_ru: string; top_contract_share_pct: number; risk_band: "lower_visible_risk" | "watch" | "elevated" | "high"; inference_basis: "contract_date_age_and_open_balance_concentration_confirmed_1c_rows"; } export interface AssistantMcpDiscoveryBusinessOverviewInventoryItemBucket { item: string; rows_with_amount: number; rows_with_quantity: number; total_amount: number; total_amount_human_ru: string; total_quantity: number; } export interface AssistantMcpDiscoveryBusinessOverviewInventoryAgingSignal { rows_matched: number; rows_with_purchase_date: number; oldest_purchase_date: string | null; latest_purchase_date: string | null; max_age_days: number | null; inference_basis: "inventory_purchase_dates_from_confirmed_1c_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition { as_of_date: string; rows_matched: number; rows_with_amount: number; rows_with_quantity: number; total_amount: number; total_amount_human_ru: string; total_quantity: number; top_items: AssistantMcpDiscoveryBusinessOverviewInventoryItemBucket[]; aging_signal: AssistantMcpDiscoveryBusinessOverviewInventoryAgingSignal | null; inference_basis: "inventory_on_hand_confirmed_1c_balance_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy { period_scope: string; as_of_date: string; sales_revenue: number; sales_revenue_human_ru: string; inventory_amount: number; inventory_amount_human_ru: string; sales_to_stock_amount_ratio: number | null; stock_to_sales_revenue_pct: number | null; inference_basis: "sales_document_revenue_vs_inventory_balance_confirmed_1c_rows"; } export interface AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy { as_of_date: string; period_scope: string; oldest_purchase_date: string; max_purchase_age_days: number; sales_to_stock_amount_ratio: number; risk_band: "lower_visible_risk" | "watch" | "elevated" | "high"; inference_basis: "purchase_date_age_and_sales_to_stock_proxy_confirmed_1c_rows"; } export interface AssistantMcpDiscoveryDerivedMetadataSurface { metadata_scope: string | null; requested_meta_types: string[]; matched_rows: number; available_entity_sets: string[]; matched_objects: string[]; selected_entity_set: string | null; selected_surface_objects: string[]; surface_family_scores: { document_evidence: number; movement_evidence: number; catalog_drilldown: number; }; downstream_route_family: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null; route_family_selection_basis: "selected_entity_set" | "dominant_surface_objects" | null; recommended_next_primitive: "query_documents" | "query_movements" | "drilldown_related_objects" | null; ambiguity_detected: boolean; ambiguity_entity_sets: string[]; surface_object_ranking_applied?: boolean; available_fields: string[]; known_limitations: string[]; inference_basis: "confirmed_1c_metadata_surface_rows"; } export interface AssistantMcpDiscoveryDerivedEntityResolution { requested_entity: string | null; resolution_status: "resolved" | "ambiguous" | "not_found"; resolved_entity: string | null; resolved_reference: string | null; matched_rows: number; checked_candidates: string[]; ambiguity_candidates: string[]; confidence: "high" | "medium" | "low" | null; inference_basis: "catalog_counterparty_search_rows"; } interface AssistantMcpDiscoveryCoverageAwareQueryResult extends AddressMcpQueryExecutorResult { coverage_limited_by_probe_limit: boolean; coverage_recovered_by_period_chunking: boolean; period_chunking_granularity: AssistantMcpDiscoveryAggregationAxis | null; period_chunk_count: number; } interface AssistantMcpDiscoveryCoverageAwareQueryExecution { result: AssistantMcpDiscoveryCoverageAwareQueryResult | null; probe_results: AssistantMcpDiscoveryProbeResult[]; query_limitations: string[]; executed_probe_count: number; } export type AssistantMcpDiscoveryPilotScope = | "metadata_inspection_v1" | "entity_resolution_search_v1" | "counterparty_movement_evidence_query_movements_v1" | "counterparty_document_evidence_query_documents_v1" | "counterparty_lifecycle_query_documents_v1" | "counterparty_value_flow_query_movements_v1" | "counterparty_supplier_payout_query_movements_v1" | "counterparty_bidirectional_value_flow_query_movements_v1" | "business_overview_route_template_v1" | "inventory_route_template_v1"; export interface AssistantMcpDiscoveryPilotExecutionContract { schema_version: typeof ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION; policy_owner: "assistantMcpDiscoveryPilotExecutor"; pilot_status: AssistantMcpDiscoveryPilotStatus; pilot_scope: AssistantMcpDiscoveryPilotScope; dry_run: AssistantMcpDiscoveryRuntimeDryRunContract; mcp_execution_performed: boolean; executed_primitives: string[]; skipped_primitives: string[]; probe_results: AssistantMcpDiscoveryProbeResult[]; evidence: AssistantMcpDiscoveryEvidenceContract; source_rows_summary: string | null; derived_metadata_surface: AssistantMcpDiscoveryDerivedMetadataSurface | null; derived_entity_resolution: AssistantMcpDiscoveryDerivedEntityResolution | null; derived_activity_period: AssistantMcpDiscoveryDerivedActivityPeriod | null; derived_ranked_value_flow?: AssistantMcpDiscoveryDerivedRankedValueFlow | null; derived_value_flow: AssistantMcpDiscoveryDerivedValueFlow | null; derived_bidirectional_value_flow: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null; derived_business_overview?: AssistantMcpDiscoveryDerivedBusinessOverview | null; query_limitations: string[]; reason_codes: string[]; } type AddressMcpQueryExecutorResult = Awaited>; const DEFAULT_DEPS: ResolvedAssistantMcpDiscoveryPilotExecutorDeps = { executeAddressMcpQuery, executeAddressMcpMetadata }; const ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT = 1000; const ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE = ` ВЫБРАТЬ ПЕРВЫЕ __LIMIT__ ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Контрагент, ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Counterparty, Контрагенты.Ссылка КАК КонтрагентСсылка, Контрагенты.Ссылка КАК CounterpartyRef, Контрагенты.Наименование КАК Наименование ИЗ Справочник.Контрагенты КАК Контрагенты `; const ENTITY_RESOLUTION_STOPWORDS = new Set([ "ооо", "ао", "зао", "ип", "llc", "ltd", "company", "контрагент", "counterparty", "поставщик", "supplier", "клиент", "customer", "в", "1с", "1c", "найди", "найти", "поищи", "search", "find" ]); function toNonEmptyString(value: unknown): string | null { if (value === null || value === undefined) { return null; } const text = String(value).trim(); return text.length > 0 ? text : null; } function normalizeReasonCode(value: string): string | null { 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: string[], value: string): void { const normalized = normalizeReasonCode(value); if (normalized && !target.includes(normalized)) { target.push(normalized); } } function pushUnique(target: string[], value: string): void { const text = value.trim(); if (text && !target.includes(text)) { target.push(text); } } function aggregationAxisForPlanner( planner: AssistantMcpDiscoveryPlannerContract ): AssistantMcpDiscoveryAggregationAxis | null { const axis = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.asked_aggregation_axis)?.toLowerCase(); return axis === "month" ? "month" : null; } function firstEntityCandidate(planner: AssistantMcpDiscoveryPlannerContract): string | null { 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: string | null): Pick { 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 rangeMatch = dateScope.match(/^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/); if (rangeMatch) { return { period_from: rangeMatch[1], period_to: rangeMatch[2] }; } 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 asOfDateFromDateScope(dateScope: string | null): string | null { if (!dateScope) { return null; } const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/); if (dateMatch) { return `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`; } const monthMatch = dateScope.match(/^(\d{4})-(\d{2})$/); if (monthMatch) { const year = Number(monthMatch[1]); const month = Number(monthMatch[2]); if (Number.isFinite(year) && Number.isFinite(month) && month >= 1 && month <= 12) { const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate(); return `${monthMatch[1]}-${monthMatch[2]}-${String(lastDay).padStart(2, "0")}`; } } const yearMatch = dateScope.match(/^(\d{4})$/); return yearMatch ? `${yearMatch[1]}-12-31` : null; } function buildLifecycleFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet { 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: AssistantMcpDiscoveryPlannerContract): AddressFilterSet { 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 buildBusinessOverviewTaxFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null { const meaning = planner.discovery_plan.turn_meaning_ref; const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); const periodFilters = dateScopeToFilters(dateScope); if (!periodFilters.period_from || !periodFilters.period_to) { return null; } return { ...periodFilters, ...(organization ? { organization } : {}) }; } function buildBusinessOverviewDebtFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null { const meaning = planner.discovery_plan.turn_meaning_ref; const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); const asOfDate = asOfDateFromDateScope(dateScope); if (!asOfDate) { return null; } return { ...dateScopeToFilters(dateScope), as_of_date: asOfDate, ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; } function buildBusinessOverviewInventoryFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null { const meaning = planner.discovery_plan.turn_meaning_ref; const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); const asOfDate = asOfDateFromDateScope(dateScope); if (!asOfDate) { return null; } return { ...dateScopeToFilters(dateScope), as_of_date: asOfDate, ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; } function buildBusinessOverviewTradingMarginFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet | null { const meaning = planner.discovery_plan.turn_meaning_ref; const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); const periodFilters = dateScopeToFilters(dateScope); if (!periodFilters.period_from || !periodFilters.period_to) { return null; } return { ...periodFilters, ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; } function buildInventoryExactFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet { const meaning = planner.discovery_plan.turn_meaning_ref; const subject = firstEntityCandidate(planner); const organization = toNonEmptyString(meaning?.explicit_organization_scope); const dateScope = toNonEmptyString(meaning?.explicit_date_scope); const asOfDate = asOfDateFromDateScope(dateScope); const filters: AddressFilterSet = { ...dateScopeToFilters(dateScope), ...(asOfDate ? { as_of_date: asOfDate } : {}), ...(organization ? { organization } : {}), limit: planner.discovery_plan.execution_budget.max_rows_per_probe, sort: "period_asc" }; if ( planner.selected_chain_id === "inventory_purchase_provenance" || planner.selected_chain_id === "inventory_sale_trace" ) { return { ...filters, ...(subject ? { item: subject } : {}) }; } if (planner.selected_chain_id === "inventory_supplier_overlap") { return { ...filters, ...(subject ? { counterparty: subject } : {}) }; } return filters; } function organizationScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string | null { return toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_organization_scope); } function rankingNeedForPlanner( planner: AssistantMcpDiscoveryPlannerContract ): AssistantMcpDiscoveryDerivedRankedValueFlow["ranking_need"] | null { const rankingNeed = toNonEmptyString(planner.data_need_graph?.ranking_need)?.toLowerCase(); if (rankingNeed === "top_desc" || rankingNeed === "bottom_asc") { return rankingNeed; } return null; } function normalizeEntityResolutionText(value: string | null): string { return String(value ?? "") .toLowerCase() .replace(/ё/g, "е") .replace(/[«»"'`]/g, " ") .replace(/[^\p{L}\p{N}\s-]+/gu, " ") .replace(/\s+/g, " ") .trim(); } function tokenizeEntityResolutionText(value: string | null): string[] { return normalizeEntityResolutionText(value) .split(" ") .map((token) => token.trim()) .filter((token) => token.length >= 2 && !ENTITY_RESOLUTION_STOPWORDS.has(token)); } function isLowQualityEntityResolutionAnchor(value: string | null): boolean { return tokenizeEntityResolutionText(value).length <= 0; } function entityResolutionCandidateName(row: Record): string | null { const candidates = [ row["Контрагент"], row["Counterparty"], row["Наименование"], row["name"], row["Name"], row["registrator"], row["Registrator"] ]; for (const candidate of candidates) { const text = toNonEmptyString(candidate); if (text) { return text; } } return null; } function entityResolutionCandidateRef(row: Record): string | null { const candidates = [row["КонтрагентСсылка"], row["CounterpartyRef"], row["ref"], row["Ref"]]; for (const candidate of candidates) { const text = toNonEmptyString(candidate); if (text) { return text; } } return null; } function scoreEntityResolutionCandidate(name: string, requested: string): number | null { const normalizedName = normalizeEntityResolutionText(name); const normalizedRequested = normalizeEntityResolutionText(requested); const requestedTokens = tokenizeEntityResolutionText(requested); if (!normalizedName || !normalizedRequested || requestedTokens.length <= 0) { return null; } let score = 0; if (normalizedName === normalizedRequested) { score += 10_000; } else if (normalizedName.includes(normalizedRequested)) { score += 5_000; } else if (normalizedRequested.includes(normalizedName) && normalizedName.length >= 4) { score += 2_000; } for (const token of requestedTokens) { if (!normalizedName.includes(token)) { return null; } score += Math.max(40, token.length * 20); } score -= Math.abs(normalizedName.length - normalizedRequested.length); return score; } function deriveEntityResolution( result: AddressMcpQueryExecutorResult | null, requestedEntity: string | null ): AssistantMcpDiscoveryDerivedEntityResolution | null { if (!result || result.error || !requestedEntity) { return null; } const checkedCandidates = uniqueCandidateStrings( result.raw_rows .map((row) => entityResolutionCandidateName(row)) .filter((value): value is string => Boolean(value)) ); const scoredCandidates = checkedCandidates .map((name) => { const score = scoreEntityResolutionCandidate(name, requestedEntity); return score === null ? null : { name, score }; }) .filter((value): value is { name: string; score: number } => Boolean(value)) .sort((left, right) => right.score - left.score || left.name.length - right.name.length || left.name.localeCompare(right.name, "ru")); if (scoredCandidates.length <= 0) { return { requested_entity: requestedEntity, resolution_status: "not_found", resolved_entity: null, resolved_reference: null, matched_rows: result.rows.length, checked_candidates: checkedCandidates.slice(0, 12), ambiguity_candidates: [], confidence: null, inference_basis: "catalog_counterparty_search_rows" }; } const bestCandidate = scoredCandidates[0]; const bestNormalized = normalizeEntityResolutionText(bestCandidate.name); const requestedNormalized = normalizeEntityResolutionText(requestedEntity); const requestedTokens = tokenizeEntityResolutionText(requestedEntity); const exactMatch = bestNormalized === requestedNormalized; const strongContains = requestedTokens.length > 1 && bestNormalized.includes(requestedNormalized); const topCandidates = scoredCandidates.filter((candidate) => candidate.score === bestCandidate.score); if (topCandidates.length > 1 && !exactMatch && !strongContains) { return { requested_entity: requestedEntity, resolution_status: "ambiguous", resolved_entity: null, resolved_reference: null, matched_rows: result.rows.length, checked_candidates: checkedCandidates.slice(0, 12), ambiguity_candidates: topCandidates.map((candidate) => candidate.name).slice(0, 6), confidence: "low", inference_basis: "catalog_counterparty_search_rows" }; } const matchedRow = result.raw_rows.find((row) => normalizeEntityResolutionText(entityResolutionCandidateName(row)) === bestNormalized) ?? null; return { requested_entity: requestedEntity, resolution_status: "resolved", resolved_entity: bestCandidate.name, resolved_reference: matchedRow ? entityResolutionCandidateRef(matchedRow) : null, matched_rows: result.rows.length, checked_candidates: checkedCandidates.slice(0, 12), ambiguity_candidates: [], confidence: exactMatch ? "high" : strongContains ? "medium" : "low", inference_basis: "catalog_counterparty_search_rows" }; } function uniqueCandidateStrings(values: string[]): string[] { const result: string[] = []; for (const value of values) { pushUnique(result, value); } return result; } function isLifecyclePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if (planner.selected_chain_id === "lifecycle") { return true; } 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 isDocumentEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if (planner.selected_chain_id === "document_evidence") { return true; } 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_documents") && (combined.includes("document") || combined.includes("list_documents")) ); } function isMovementEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if (planner.selected_chain_id === "movement_evidence") { return true; } 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 semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase(); const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`; return ( planner.proposed_primitives.includes("query_movements") && (combined.includes("movement") || combined.includes("movements") || combined.includes("bank_operations") || combined.includes("movement_evidence") || combined.includes("list_movements")) ); } function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if ( planner.selected_chain_id === "value_flow" || planner.selected_chain_id === "value_flow_ranking" || planner.selected_chain_id === "value_flow_comparison" ) { return true; } 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 isBusinessOverviewPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { return planner.selected_chain_id === "business_overview"; } function isInventoryPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { return ( planner.selected_chain_id === "inventory_stock_snapshot" || planner.selected_chain_id === "inventory_supplier_overlap" || planner.selected_chain_id === "inventory_purchase_provenance" || planner.selected_chain_id === "inventory_sale_trace" ); } function isMetadataPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if ( planner.selected_chain_id === "metadata_inspection" || planner.selected_chain_id === "metadata_lane_clarification" || planner.selected_chain_id === "catalog_drilldown" ) { return true; } 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 semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase(); const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`; return ( planner.proposed_primitives.includes("inspect_1c_metadata") && (combined.includes("metadata") || combined.includes("schema") || combined.includes("catalog") || combined.includes("inspect_documents") || combined.includes("inspect_registers") || combined.includes("inspect_fields")) ); } function isEntityResolutionPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean { if (planner.selected_chain_id === "entity_resolution") { return true; } 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 semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase(); const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`; return ( planner.proposed_primitives.includes("search_business_entity") && (combined.includes("entity_resolution") || combined.includes("search_business_entity") || combined.includes("entity discovery") || combined.includes("counterparty search")) ); } function metadataScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string | null { const entityCandidate = firstEntityCandidate(planner); if (entityCandidate) { return entityCandidate; } if (planner.selected_chain_id === "catalog_drilldown") { const surface = planner.metadata_surface_ref; const scopeCandidate = [ ...(surface?.selected_surface_objects ?? []), surface?.selected_entity_set ?? "" ] .map((value) => toNonEmptyString(value)) .filter((value): value is string => Boolean(value)) .map((value) => { const parts = value.split(".").map((item) => item.trim()).filter((item) => item.length > 0); return parts.length > 0 ? parts[parts.length - 1] ?? value : value; }) .find((value) => value.length > 0); if (scopeCandidate) { return scopeCandidate; } } const meaning = planner.discovery_plan.turn_meaning_ref; const combined = `${meaning?.asked_domain_family ?? ""} ${meaning?.asked_action_family ?? ""} ${meaning?.unsupported_but_understood_family ?? ""}` .toLowerCase() .trim(); if (combined.includes("vat")) { return "НДС"; } if (combined.includes("inventory")) { return "склад"; } if (combined.includes("counterparty")) { return "контрагент"; } return null; } function metadataTypesForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string[] { if (planner.selected_chain_id === "catalog_drilldown") { const selectedEntitySet = toNonEmptyString(planner.metadata_surface_ref?.selected_entity_set); if (selectedEntitySet) { return [selectedEntitySet]; } } const meaning = planner.discovery_plan.turn_meaning_ref; const action = String(meaning?.asked_action_family ?? "").toLowerCase(); if (action === "inspect_registers") { return ["РегистрНакопления", "РегистрСведений"]; } if (action === "inspect_documents") { return ["Документ"]; } if (action === "inspect_catalog") { return ["Справочник"]; } return ["Документ", "РегистрНакопления", "РегистрСведений", "Справочник"]; } function metadataScopeRankingAllowedForPlanner(planner: AssistantMcpDiscoveryPlannerContract): boolean { const action = String(planner.discovery_plan.turn_meaning_ref?.asked_action_family ?? "").toLowerCase().trim(); return action === "inspect_surface"; } interface ValueFlowPilotProfile { scope: Extract< AssistantMcpDiscoveryPilotScope, | "counterparty_value_flow_query_movements_v1" | "counterparty_supplier_payout_query_movements_v1" | "counterparty_bidirectional_value_flow_query_movements_v1" >; recipe_intent: Extract | null; direction: AssistantMcpDiscoveryDerivedValueFlow["value_flow_direction"] | "bidirectional_net_value_flow"; } function valueFlowPilotProfile(planner: AssistantMcpDiscoveryPlannerContract): ValueFlowPilotProfile { const meaning = planner.discovery_plan.turn_meaning_ref; const action = String(meaning?.asked_action_family ?? "").toLowerCase(); const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase(); const combined = `${action} ${unsupported}`; if ( combined.includes("net_value_flow") || combined.includes("bidirectional") || combined.includes("netting") || combined.includes("net") ) { return { scope: "counterparty_bidirectional_value_flow_query_movements_v1", recipe_intent: null, direction: "bidirectional_net_value_flow" }; } if ( combined.includes("payout") || combined.includes("outflow") || combined.includes("supplier") || combined.includes("paid") ) { return { scope: "counterparty_supplier_payout_query_movements_v1", recipe_intent: "supplier_payouts_profile", direction: "outgoing_supplier_payout" }; } return { scope: "counterparty_value_flow_query_movements_v1", recipe_intent: "customer_revenue_and_payments", direction: "incoming_customer_revenue" }; } function inventoryIntentForPlanner(planner: AssistantMcpDiscoveryPlannerContract): AddressIntent | null { switch (planner.selected_chain_id) { case "inventory_stock_snapshot": return "inventory_on_hand_as_of_date"; case "inventory_supplier_overlap": return "inventory_supplier_stock_overlap_as_of_date"; case "inventory_purchase_provenance": return "inventory_purchase_provenance_for_item"; case "inventory_sale_trace": return "inventory_sale_trace_for_item"; default: return null; } } function inventoryExecutablePrimitiveForPlanner( planner: AssistantMcpDiscoveryPlannerContract ): AssistantMcpDiscoveryRuntimeStepContract["primitive_id"] | null { switch (planner.selected_chain_id) { case "inventory_stock_snapshot": case "inventory_supplier_overlap": return "query_movements"; case "inventory_purchase_provenance": case "inventory_sale_trace": return "query_documents"; default: return null; } } function skippedProbeResult(step: AssistantMcpDiscoveryRuntimeStepContract, limitation: string): AssistantMcpDiscoveryProbeResult { return { primitive_id: step.primitive_id, status: "skipped", rows_received: 0, rows_matched: 0, limitation }; } function queryResultToProbeResult( primitiveId: string, result: AddressMcpQueryExecutorResult ): AssistantMcpDiscoveryProbeResult { return { primitive_id: primitiveId, status: result.error ? "error" : "ok", rows_received: result.fetched_rows, rows_matched: result.matched_rows, limitation: result.error }; } function metadataResultToProbeResult( primitiveId: string, result: AddressMcpMetadataRowsResult ): AssistantMcpDiscoveryProbeResult { return { primitive_id: primitiveId, status: result.error ? "error" : "ok", rows_received: result.fetched_rows, rows_matched: result.error ? 0 : result.rows.length, limitation: result.error }; } function toCoverageAwareQueryResult( result: AddressMcpQueryExecutorResult | null, options: { coverageLimitedByProbeLimit?: boolean; coverageRecoveredByPeriodChunking?: boolean; periodChunkingGranularity?: AssistantMcpDiscoveryAggregationAxis | null; periodChunkCount?: number; } = {} ): AssistantMcpDiscoveryCoverageAwareQueryResult | null { if (!result) { return null; } return { ...result, coverage_limited_by_probe_limit: options.coverageLimitedByProbeLimit ?? false, coverage_recovered_by_period_chunking: options.coverageRecoveredByPeriodChunking ?? false, period_chunking_granularity: options.periodChunkingGranularity ?? null, period_chunk_count: options.periodChunkCount ?? 0 }; } function monthWindowsForYear(year: string): Array<{ period_from: string; period_to: string }> { const result: Array<{ period_from: string; period_to: string }> = []; for (let month = 0; month < 12; month += 1) { const start = new Date(Date.UTC(Number(year), month, 1)); const end = new Date(Date.UTC(Number(year), month + 1, 0)); result.push({ period_from: `${start.getUTCFullYear()}-${String(start.getUTCMonth() + 1).padStart(2, "0")}-${String(start.getUTCDate()).padStart(2, "0")}`, period_to: `${end.getUTCFullYear()}-${String(end.getUTCMonth() + 1).padStart(2, "0")}-${String(end.getUTCDate()).padStart(2, "0")}` }); } return result; } function periodWindowsForDateScope(dateScope: string | null): Array<{ period_from: string; period_to: string }> { const yearMatch = dateScope?.match(/^(\d{4})$/); if (yearMatch) { return monthWindowsForYear(yearMatch[1]); } return []; } function mergeCoverageAwareQueryResults( results: AddressMcpQueryExecutorResult[], options: { coverageLimitedByProbeLimit: boolean; coverageRecoveredByPeriodChunking: boolean; periodChunkingGranularity: AssistantMcpDiscoveryAggregationAxis | null; periodChunkCount: number; } ): AssistantMcpDiscoveryCoverageAwareQueryResult { const rawRows = results.flatMap((item) => item.raw_rows); const rows = results.flatMap((item) => item.rows); const errors = results.map((item) => toNonEmptyString(item.error)).filter((item): item is string => Boolean(item)); return { fetched_rows: results.reduce((sum, item) => sum + item.fetched_rows, 0), matched_rows: results.reduce((sum, item) => sum + item.matched_rows, 0), raw_rows: rawRows, rows, error: errors[0] ?? null, coverage_limited_by_probe_limit: options.coverageLimitedByProbeLimit, coverage_recovered_by_period_chunking: options.coverageRecoveredByPeriodChunking, period_chunking_granularity: options.periodChunkingGranularity, period_chunk_count: options.periodChunkCount }; } async function executeCoverageAwareValueFlowQuery(input: { primitiveId: string; recipePlanBuilder: (filters: AddressFilterSet) => { query: string; limit: number; account_scope?: string[]; }; baseFilters: AddressFilterSet; dateScope: string | null; maxProbeCount: number; maxRowsPerProbe: number; deps: ResolvedAssistantMcpDiscoveryPilotExecutorDeps; }): Promise { const queryLimitations: string[] = []; const probeResults: AssistantMcpDiscoveryProbeResult[] = []; let executedProbeCount = 0; const broadRecipePlan = input.recipePlanBuilder(input.baseFilters); const broadResult = await input.deps.executeAddressMcpQuery({ query: broadRecipePlan.query, limit: broadRecipePlan.limit, account_scope: broadRecipePlan.account_scope }); executedProbeCount += 1; probeResults.push(queryResultToProbeResult(input.primitiveId, broadResult)); const broadCoverageLimited = !broadResult.error && broadResult.matched_rows >= input.maxRowsPerProbe; if (broadResult.error) { pushUnique(queryLimitations, broadResult.error); return { result: toCoverageAwareQueryResult(broadResult, { coverageLimitedByProbeLimit: false }), probe_results: probeResults, query_limitations: queryLimitations, executed_probe_count: executedProbeCount }; } const periodWindows = periodWindowsForDateScope(input.dateScope); if (!broadCoverageLimited || periodWindows.length === 0) { return { result: toCoverageAwareQueryResult(broadResult, { coverageLimitedByProbeLimit: broadCoverageLimited }), probe_results: probeResults, query_limitations: queryLimitations, executed_probe_count: executedProbeCount }; } const requiredChunkProbeCount = periodWindows.length; if (executedProbeCount + requiredChunkProbeCount > input.maxProbeCount) { pushUnique( queryLimitations, "Requested period hit the MCP row limit, but the approved monthly recovery probe budget is smaller than the required subperiod count" ); return { result: toCoverageAwareQueryResult(broadResult, { coverageLimitedByProbeLimit: true }), probe_results: probeResults, query_limitations: queryLimitations, executed_probe_count: executedProbeCount }; } const chunkResults: AddressMcpQueryExecutorResult[] = []; let anyChunkLimited = false; let anyChunkError = false; for (const window of periodWindows) { const chunkFilters: AddressFilterSet = { ...input.baseFilters, period_from: window.period_from, period_to: window.period_to }; const chunkPlan = input.recipePlanBuilder(chunkFilters); const chunkResult = await input.deps.executeAddressMcpQuery({ query: chunkPlan.query, limit: chunkPlan.limit, account_scope: chunkPlan.account_scope }); executedProbeCount += 1; probeResults.push(queryResultToProbeResult(input.primitiveId, chunkResult)); if (chunkResult.error) { anyChunkError = true; pushUnique(queryLimitations, chunkResult.error); continue; } if (chunkResult.matched_rows >= input.maxRowsPerProbe) { anyChunkLimited = true; } chunkResults.push(chunkResult); } if (chunkResults.length === 0 && anyChunkError) { return { result: toCoverageAwareQueryResult(broadResult, { coverageLimitedByProbeLimit: true }), probe_results: probeResults, query_limitations: queryLimitations, executed_probe_count: executedProbeCount }; } return { result: mergeCoverageAwareQueryResults(chunkResults, { coverageLimitedByProbeLimit: anyChunkLimited || anyChunkError, coverageRecoveredByPeriodChunking: true, periodChunkingGranularity: "month", periodChunkCount: periodWindows.length }), probe_results: probeResults, query_limitations: queryLimitations, executed_probe_count: executedProbeCount }; } function summarizeLifecycleRows(result: AddressMcpQueryExecutorResult): string | null { 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 summarizeDocumentRows(result: AddressMcpQueryExecutorResult): string | null { 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 document scope`; } function summarizeMovementRows(result: AddressMcpQueryExecutorResult): string | null { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP movement rows fetched"; } return `${result.fetched_rows} MCP movement rows fetched, ${result.matched_rows} matched movement scope`; } function summarizeValueFlowRows(result: AssistantMcpDiscoveryCoverageAwareQueryResult): string | null { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP value-flow rows fetched"; } if (result.coverage_recovered_by_period_chunking && result.period_chunking_granularity === "month") { return `${result.period_chunk_count} monthly MCP value-flow probes fetched ${result.fetched_rows} rows total, ${result.matched_rows} matched value-flow scope after the broad probe hit the row limit`; } return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`; } function summarizeInventoryRows(result: AddressMcpQueryExecutorResult): string | null { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP inventory exact rows fetched"; } return `${result.fetched_rows} MCP inventory exact rows fetched, ${result.matched_rows} matched inventory scope`; } function summarizeMetadataRows(result: AddressMcpMetadataRowsResult): string | null { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP metadata rows fetched"; } return `${result.fetched_rows} MCP metadata rows fetched`; } function summarizeEntityResolutionRows(result: AddressMcpQueryExecutorResult): string | null { if (result.error) { return null; } if (result.fetched_rows <= 0) { return "0 MCP catalog rows fetched"; } return `${result.fetched_rows} MCP catalog rows fetched for entity search`; } function entityResolutionFollowupStepLimitation(): string { return "Entity-resolution could not continue because the checked catalog search step did not return a confirmed slice"; } function buildEntityResolutionResolveProbeResult(input: { queryResult: AddressMcpQueryExecutorResult; resolution: AssistantMcpDiscoveryDerivedEntityResolution | null; }): AssistantMcpDiscoveryProbeResult { if (!input.resolution) { return { primitive_id: "resolve_entity_reference", status: "ok", rows_received: input.queryResult.fetched_rows, rows_matched: 0, limitation: null }; } if (input.resolution.resolution_status === "resolved") { return { primitive_id: "resolve_entity_reference", status: "ok", rows_received: input.queryResult.fetched_rows, rows_matched: 1, limitation: null }; } return { primitive_id: "resolve_entity_reference", status: "ok", rows_received: input.queryResult.fetched_rows, rows_matched: 0, limitation: null }; } function buildEntityResolutionCoverageProbeResult(input: { resolution: AssistantMcpDiscoveryDerivedEntityResolution | null; }): AssistantMcpDiscoveryProbeResult { const resolved = input.resolution?.resolution_status === "resolved"; return { primitive_id: "probe_coverage", status: "ok", rows_received: 1, rows_matched: resolved ? 1 : 0, limitation: null }; } function metadataRowText(row: Record, keys: string[]): string | null { for (const key of keys) { const text = toNonEmptyString(row[key]); if (text) { return text; } } return null; } function metadataObjectName(row: Record): string | null { return metadataRowText(row, [ "ПолноеИмя", "full_name", "FullName", "Имя", "name", "Name", "presentation", "Представление", "synonym", "Synonym" ]); } function metadataEntitySet(row: Record): string | null { return metadataRowText(row, [ "ТипМетаданных", "type", "Type", "meta_type", "MetaType", "ВидМетаданных", "kind" ]); } function inferMetadataEntitySetFromObjectName(objectName: string | null): string | null { const text = String(objectName ?? "").trim(); if (!text) { return null; } const dotIndex = text.indexOf("."); if (dotIndex <= 0) { return null; } const entitySet = text.slice(0, dotIndex).trim(); return entitySet.length > 0 ? entitySet : null; } function metadataChildNames(value: unknown): string[] { if (!Array.isArray(value)) { return []; } const result: string[] = []; for (const item of value) { if (!item || typeof item !== "object" || Array.isArray(item)) { continue; } const record = item as Record; const fieldName = metadataRowText(record, ["Имя", "name", "Name", "full_name", "FullName"]); if (fieldName) { pushUnique(result, fieldName); } } return result; } function metadataAvailableFields(rows: Array>): string[] { const result: string[] = []; for (const row of rows) { for (const field of metadataChildNames(row["Реквизиты"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["attributes"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["Attributes"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["Измерения"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["dimensions"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["Ресурсы"])) { pushUnique(result, field); } for (const field of metadataChildNames(row["resources"])) { pushUnique(result, field); } } return result; } function normalizeMetadataEntitySetToken(value: string): string { return String(value ?? "") .toLowerCase() .replace(/[\s_.-]+/g, ""); } function metadataMatchesRequestedType(entitySet: string, requestedMetaType: string): boolean { const entityToken = normalizeMetadataEntitySetToken(entitySet); const requestedToken = normalizeMetadataEntitySetToken(requestedMetaType); return entityToken.includes(requestedToken) || requestedToken.includes(entityToken); } function metadataRouteFamilyForEntitySet( entitySet: string ): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null { const token = normalizeMetadataEntitySetToken(entitySet); if (token.includes("документ") || token.includes("document")) { return "document_evidence"; } if ( token.includes("регистрнакопления") || token.includes("регистсведений") || token.includes("регистрсведений") || token.includes("accumulationregister") || token.includes("informationregister") ) { return "movement_evidence"; } if (token.includes("справочник") || token.includes("catalog") || token.includes("directory")) { return "catalog_drilldown"; } return null; } function metadataRouteFamilyForEntitySetRelaxed( entitySet: string ): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null { const strict = metadataRouteFamilyForEntitySet(entitySet); if (strict) { return strict; } const raw = String(entitySet ?? "").trim(); if (!raw) { return null; } if (raw.includes("Документ") || raw.includes("Документ")) { return "document_evidence"; } if ( raw.includes("РегистрНакопления") || raw.includes("РегистрСведений") || raw.includes("РегистрНакопления") || raw.includes("РегистрСведений") ) { return "movement_evidence"; } if (raw.includes("Справочник") || raw.includes("Справочник")) { return "catalog_drilldown"; } return null; } function metadataNextPrimitiveForRouteFamily( routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null ): "query_documents" | "query_movements" | "drilldown_related_objects" | null { if (routeFamily === "document_evidence") { return "query_documents"; } if (routeFamily === "movement_evidence") { return "query_movements"; } if (routeFamily === "catalog_drilldown") { return "drilldown_related_objects"; } return null; } function selectMetadataEntityGrounding( availableEntitySets: string[], requestedMetaTypes: string[] ): { selectedEntitySet: string | null; ambiguityDetected: boolean; ambiguityEntitySets: string[]; } { const requestedMatches = availableEntitySets.filter((entitySet) => requestedMetaTypes.some((requestedMetaType) => metadataMatchesRequestedType(entitySet, requestedMetaType)) ); if (requestedMatches.length === 1) { return { selectedEntitySet: requestedMatches[0] ?? null, ambiguityDetected: false, ambiguityEntitySets: [] }; } if (requestedMatches.length > 1) { return { selectedEntitySet: null, ambiguityDetected: true, ambiguityEntitySets: requestedMatches }; } if (availableEntitySets.length === 1) { return { selectedEntitySet: availableEntitySets[0] ?? null, ambiguityDetected: false, ambiguityEntitySets: [] }; } return { selectedEntitySet: null, ambiguityDetected: availableEntitySets.length > 1, ambiguityEntitySets: availableEntitySets }; } function metadataObjectsForEntitySet(entitySet: string | null, matchedObjects: string[]): string[] { if (!entitySet) { return []; } return matchedObjects.filter((item) => item.startsWith(`${entitySet}.`) || item.includes(entitySet)); } function emptyMetadataSurfaceFamilyScores(): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] { return { document_evidence: 0, movement_evidence: 0, catalog_drilldown: 0 }; } function metadataSurfaceFamilyScores( matchedObjects: string[] ): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] { const scores = emptyMetadataSurfaceFamilyScores(); for (const objectName of matchedObjects) { const entitySet = inferMetadataEntitySetFromObjectName(objectName); const routeFamily = entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) : null; if (routeFamily) { scores[routeFamily] += 1; } } return scores; } function normalizeMetadataObjectRankingToken(value: string): string { return String(value ?? "") .toLowerCase() .replace(/[^\p{L}\p{N}]+/gu, ""); } function metadataScopeRankingTokens(metadataScope: string | null): string[] { const scope = String(metadataScope ?? "").trim(); if (!scope) { return []; } const condensed = normalizeMetadataObjectRankingToken(scope); const result: string[] = []; if (condensed.length >= 2) { pushUnique(result, condensed); } for (const token of scope.toLowerCase().split(/[^\p{L}\p{N}]+/gu)) { const normalized = normalizeMetadataObjectRankingToken(token); if (normalized.length >= 2) { pushUnique(result, normalized); } } return result; } function metadataObjectRelevanceScore(metadataScope: string | null, objectName: string): number { const objectToken = normalizeMetadataObjectRankingToken(objectName); if (!objectToken) { return 1; } let score = 1; for (const token of metadataScopeRankingTokens(metadataScope)) { if (objectToken.includes(token)) { score += token.length >= 6 ? 4 : 3; } } return score; } function metadataWeightedSurfaceFamilyScores( matchedObjects: string[], metadataScope: string | null ): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] { const scores = emptyMetadataSurfaceFamilyScores(); for (const objectName of matchedObjects) { const entitySet = inferMetadataEntitySetFromObjectName(objectName); const routeFamily = entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) : null; if (routeFamily) { scores[routeFamily] += metadataObjectRelevanceScore(metadataScope, objectName); } } return scores; } function sortMetadataObjectsByRelevance(matchedObjects: string[], metadataScope: string | null): string[] { return [...matchedObjects].sort((left, right) => { const scoreDelta = metadataObjectRelevanceScore(metadataScope, right) - metadataObjectRelevanceScore(metadataScope, left); if (scoreDelta !== 0) { return scoreDelta; } return left.localeCompare(right, "ru"); }); } function metadataObjectsForRouteFamily( routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null, matchedObjects: string[], metadataScope: string | null ): string[] { if (!routeFamily) { return []; } const filtered = matchedObjects.filter((objectName) => { const entitySet = inferMetadataEntitySetFromObjectName(objectName); return entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) === routeFamily : false; }); return sortMetadataObjectsByRelevance(filtered, metadataScope); } function selectDominantMetadataRouteFamilyFromScores( scores: AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] ): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null { const ranked = (Object.entries(scores) as Array< ["document_evidence" | "movement_evidence" | "catalog_drilldown", number] >) .filter(([, score]) => score > 0) .sort((left, right) => right[1] - left[1]); const top = ranked[0]; const second = ranked[1]; if (!top) { return null; } if (!second) { return top[0]; } const absoluteMargin = top[1] - second[1]; const relativeRatio = second[1] > 0 ? top[1] / second[1] : Number.POSITIVE_INFINITY; const clearlyDominant = absoluteMargin >= 2 || relativeRatio >= 1.5; return clearlyDominant ? top[0] : null; } function selectMetadataRouteFamilyFromSurfaceScores(input: { matchedObjects: string[]; metadataScope: string | null; countScores: AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"]; allowScopeRanking: boolean; }): { routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null; rankingApplied: boolean; } { const countDominant = selectDominantMetadataRouteFamilyFromScores(input.countScores); if (countDominant) { return { routeFamily: countDominant, rankingApplied: false }; } if (!input.allowScopeRanking) { return { routeFamily: null, rankingApplied: false }; } const rankedCounts = (Object.entries(input.countScores) as Array< ["document_evidence" | "movement_evidence" | "catalog_drilldown", number] >) .filter(([, score]) => score > 0) .sort((left, right) => right[1] - left[1]); const topCount = rankedCounts[0]?.[1] ?? 0; const secondCount = rankedCounts[1]?.[1] ?? 0; if (topCount <= 0 || topCount !== secondCount) { return { routeFamily: null, rankingApplied: false }; } const weightedScores = metadataWeightedSurfaceFamilyScores(input.matchedObjects, input.metadataScope); const weightedDominant = selectDominantMetadataRouteFamilyFromScores(weightedScores); return { routeFamily: weightedDominant, rankingApplied: Boolean(weightedDominant) }; } function deriveMetadataSurface( result: AddressMcpMetadataRowsResult | null, metadataScope: string | null, requestedMetaTypes: string[], allowScopeRanking: boolean ): AssistantMcpDiscoveryDerivedMetadataSurface | null { if (!result || result.error || result.rows.length <= 0) { return null; } const matchedObjects: string[] = []; const availableEntitySets: string[] = []; for (const row of result.rows) { const objectName = metadataObjectName(row); if (objectName) { pushUnique(matchedObjects, objectName); } const entitySet = metadataEntitySet(row) ?? inferMetadataEntitySetFromObjectName(objectName); if (entitySet) { pushUnique(availableEntitySets, entitySet); } } const grounding = selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes); const surfaceFamilyScores = metadataSurfaceFamilyScores(matchedObjects); const selectedEntitySetRouteFamily = grounding.selectedEntitySet ? metadataRouteFamilyForEntitySetRelaxed(grounding.selectedEntitySet) : null; const scoredRouteSelection = selectedEntitySetRouteFamily === null ? selectMetadataRouteFamilyFromSurfaceScores({ matchedObjects, metadataScope, countScores: surfaceFamilyScores, allowScopeRanking }) : { routeFamily: null, rankingApplied: false }; const scoredRouteFamily = scoredRouteSelection.routeFamily; const downstreamRouteFamily = selectedEntitySetRouteFamily ?? scoredRouteFamily; const routeFamilySelectionBasis = selectedEntitySetRouteFamily ? "selected_entity_set" : scoredRouteFamily ? "dominant_surface_objects" : null; const selectedSurfaceObjects = grounding.selectedEntitySet !== null ? sortMetadataObjectsByRelevance(metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects), metadataScope) : metadataObjectsForRouteFamily(downstreamRouteFamily, matchedObjects, metadataScope); const knownLimitations: string[] = []; const ambiguityRemainsUnresolved = grounding.ambiguityDetected && !downstreamRouteFamily; if (ambiguityRemainsUnresolved && grounding.ambiguityEntitySets.length > 0) { knownLimitations.push( `Exact downstream metadata surface remains ambiguous across: ${grounding.ambiguityEntitySets.join(", ")}` ); } if (grounding.ambiguityDetected && downstreamRouteFamily && routeFamilySelectionBasis === "dominant_surface_objects") { knownLimitations.push( `Metadata surface spans multiple object sets, but dominant confirmed objects point to ${downstreamRouteFamily}` ); } return { metadata_scope: metadataScope, requested_meta_types: requestedMetaTypes, matched_rows: result.rows.length, available_entity_sets: availableEntitySets, matched_objects: matchedObjects, selected_entity_set: grounding.selectedEntitySet, selected_surface_objects: selectedSurfaceObjects, surface_family_scores: surfaceFamilyScores, downstream_route_family: downstreamRouteFamily, route_family_selection_basis: routeFamilySelectionBasis, recommended_next_primitive: metadataNextPrimitiveForRouteFamily(downstreamRouteFamily), ambiguity_detected: ambiguityRemainsUnresolved, ambiguity_entity_sets: ambiguityRemainsUnresolved ? grounding.ambiguityEntitySets : [], surface_object_ranking_applied: scoredRouteSelection.rankingApplied, available_fields: metadataAvailableFields(result.rows), known_limitations: knownLimitations, inference_basis: "confirmed_1c_metadata_surface_rows" }; } function buildMetadataConfirmedFacts( surface: AssistantMcpDiscoveryDerivedMetadataSurface | null ): string[] { if (!surface) { return []; } const facts: string[] = []; const scopeSuffix = surface.metadata_scope ? ` for ${surface.metadata_scope}` : ""; facts.push( `Confirmed 1C metadata surface${scopeSuffix}: ${surface.matched_rows} rows and ${surface.matched_objects.length} matching objects` ); if (surface.available_entity_sets.length > 0) { facts.push(`Available metadata object sets: ${surface.available_entity_sets.join(", ")}`); } if (surface.selected_entity_set) { facts.push(`Selected metadata entity set: ${surface.selected_entity_set}`); } if (surface.selected_surface_objects.length > 0) { facts.push(`Selected metadata objects: ${surface.selected_surface_objects.slice(0, 8).join(", ")}`); } if ( surface.surface_family_scores.document_evidence > 0 || surface.surface_family_scores.movement_evidence > 0 || surface.surface_family_scores.catalog_drilldown > 0 ) { facts.push( `Metadata surface family scores: document=${surface.surface_family_scores.document_evidence}, movement=${surface.surface_family_scores.movement_evidence}, catalog=${surface.surface_family_scores.catalog_drilldown}` ); } if (surface.available_fields.length > 0) { facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`); } return facts; } function buildMetadataInferredFacts( surface: AssistantMcpDiscoveryDerivedMetadataSurface | null ): string[] { if (!surface || !surface.downstream_route_family || !surface.recommended_next_primitive) { return []; } return [ `A likely next checked lane may be inferred as ${surface.downstream_route_family} from the confirmed metadata surface` ]; } function buildMetadataUnknownFacts( surface: AssistantMcpDiscoveryDerivedMetadataSurface | null, metadataScope: string | null ): string[] { if (surface) { if (surface.ambiguity_detected && surface.ambiguity_entity_sets.length > 0) { return [...surface.known_limitations]; } if (surface.available_fields.length > 0) { return []; } return ["Detailed metadata fields were not returned by this MCP metadata probe"]; } if (metadataScope) { return [`No matching 1C metadata objects were confirmed for scope "${metadataScope}"`]; } return ["No matching 1C metadata objects were confirmed by this MCP metadata probe"]; } function buildEntityResolutionConfirmedFacts( resolution: AssistantMcpDiscoveryDerivedEntityResolution | null ): string[] { if (!resolution || resolution.resolution_status !== "resolved" || !resolution.resolved_entity) { return []; } if (resolution.requested_entity && normalizeEntityResolutionText(resolution.requested_entity) === normalizeEntityResolutionText(resolution.resolved_entity)) { return [`В проверенном каталожном срезе 1С найден контрагент: ${resolution.resolved_entity}`]; } return [ `В проверенном каталожном срезе 1С найден наиболее вероятный контрагент: ${resolution.resolved_entity}` ]; } function buildEntityResolutionInferredFacts( resolution: AssistantMcpDiscoveryDerivedEntityResolution | null ): string[] { if (!resolution) { return []; } if (resolution.resolution_status === "resolved") { const facts = ["Пока проверено только заземление сущности по каталогу 1С; документы, движения и денежные показатели еще не проверялись"]; if (resolution.requested_entity && resolution.resolved_entity) { const requestedNormalized = normalizeEntityResolutionText(resolution.requested_entity); const resolvedNormalized = normalizeEntityResolutionText(resolution.resolved_entity); if (requestedNormalized !== resolvedNormalized) { facts.push("Контрагент выбран как ближайшее подтвержденное совпадение имени в проверенном каталоге 1С"); } } return facts; } if (resolution.resolution_status === "ambiguous") { return ["В проверенном каталожном срезе осталось несколько близких кандидатов, поэтому точного контрагента в 1С еще нужно уточнить"]; } return []; } function buildEntityResolutionUnknownFacts( resolution: AssistantMcpDiscoveryDerivedEntityResolution | null, requestedEntity: string | null ): string[] { if (!resolution) { return ["По проверенному каталожному поиску 1С не удалось заземлить сущность контрагента"]; } const unknownFacts = ["Документы, движения и денежные показатели по этому контрагенту еще не проверялись; пока был только каталожный поиск"]; if (resolution.resolution_status === "ambiguous" && resolution.ambiguity_candidates.length > 0) { unknownFacts.unshift( `Точное заземление контрагента в 1С остается неоднозначным между вариантами: ${resolution.ambiguity_candidates.join(", ")}` ); return unknownFacts; } if (resolution.resolution_status === "not_found") { unknownFacts.unshift( requestedEntity ? `В проверенном каталожном срезе 1С не подтвержден контрагент с именем "${requestedEntity}"` : "В проверенном каталожном срезе 1С не подтвержден подходящий контрагент" ); } return unknownFacts; } function rowDateValue(row: Record): string | null { 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: Record): number | null { 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 rowNumberValue(row: Record, keys: string[]): number | null { for (const key of keys) { const candidate = row[key]; 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 rowTextValue(row: Record, keys: string[]): string | null { for (const key of keys) { const text = toNonEmptyString(row[key]); if (text) { return text; } } return null; } function rowInventoryItemValue(row: Record): string | null { return rowTextValue(row, ["Номенклатура", "Item", "item", "Товар", "Product", "product"]); } function rowWarehouseValue(row: Record): string | null { return rowTextValue(row, ["Склад", "Warehouse", "warehouse"]); } function rowDocumentValue(row: Record): string | null { return rowTextValue(row, ["Регистратор", "Registrator", "registrator", "Документ", "Document", "document"]); } function rowAccountValue(row: Record): string | null { return rowTextValue(row, ["СчетДт", "AccountDt", "account_dt", "Счет", "Account", "account"]); } function rowDebitAccountValue(row: Record): string | null { return rowTextValue(row, ["СчетДт", "AccountDt", "account_dt", "DebitAccount", "debit_account"]); } function rowCreditAccountValue(row: Record): string | null { return rowTextValue(row, ["СчетКт", "AccountKt", "account_kt", "CreditAccount", "credit_account"]); } function accountTextMatchesPrefix(account: string | null, prefixes: string[]): boolean { const normalized = String(account ?? "").replace(/\s+/g, ""); return prefixes.some((prefix) => normalized.startsWith(prefix)); } function rowQuantityValue(row: Record): number | null { return rowNumberValue(row, ["Количество", "Quantity", "quantity", "Qty", "qty", "Остаток", "Balance", "balance"]); } function rowAnalyticsTextValues(row: Record): string[] { const values: string[] = []; const analytics = row["analytics"]; if (Array.isArray(analytics)) { for (const item of analytics) { const text = toNonEmptyString(item); if (text && !values.includes(text)) { values.push(text); } } } for (const key of [ "СубконтоДт1", "СубконтоДт2", "СубконтоДт3", "СубконтоКт1", "СубконтоКт2", "СубконтоКт3", "SubcontoDt1", "SubcontoDt2", "SubcontoDt3", "SubcontoKt1", "SubcontoKt2", "SubcontoKt3" ]) { const text = toNonEmptyString(row[key]); if (text && !values.includes(text)) { values.push(text); } } return values; } function isEmptyAnalyticToken(value: string): boolean { return /^(?:0|<пусто>|пустая ссылка)$/iu.test(value.trim()); } function isLikelyContractToken(value: string): boolean { const normalized = value.trim(); if (!normalized || isEmptyAnalyticToken(normalized)) { return false; } if (/(?:договор|contract|дог\.)/iu.test(normalized)) { return true; } if (/^\d{4}-\d{2}-\d{2}/.test(normalized)) { return false; } return normalized.length >= 3 && /[\\/]/.test(normalized); } function isLikelyCounterpartyToken(value: string): boolean { const normalized = value.trim(); if (!normalized || isEmptyAnalyticToken(normalized)) { return false; } if (/^\d{4}-\d{2}-\d{2}/.test(normalized)) { return false; } if (/^\d+(?:[./-]\d+)*$/.test(normalized)) { return false; } if (!/[a-zа-я]/iu.test(normalized)) { return false; } if (/(?:договор|contract|дог\.|документ|операц|счет[-\s]?фактур|накладн|акт|поступлен|списани|плат[её]ж|банк|касса|movement|invoice|payment)/iu.test(normalized)) { return false; } return true; } function rowContractValue(row: Record): string | null { const explicit = rowTextValue(row, ["Договор", "Contract", "contract"]); if (explicit && !isEmptyAnalyticToken(explicit)) { return explicit; } return rowAnalyticsTextValues(row).find(isLikelyContractToken) ?? null; } function rowCounterpartyValue(row: Record): string | null { const candidates = [row["Контрагент"], row["Counterparty"], row["counterparty"], row["Наименование"], row["name"]]; for (const candidate of candidates) { const text = toNonEmptyString(candidate); if (text) { return text; } } return rowAnalyticsTextValues(row).find(isLikelyCounterpartyToken) ?? null; } function normalizeDateParts(yearText: string, monthText: string, dayText: string): string | null { const year = Number(yearText); const month = Number(monthText); const day = Number(dayText); if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) { return null; } if (year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > 31) { return null; } const date = new Date(Date.UTC(year, month - 1, day)); if ( date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day ) { return null; } return [ String(year).padStart(4, "0"), String(month).padStart(2, "0"), String(day).padStart(2, "0") ].join("-"); } function extractContractDateFromText(value: string | null): string | null { const text = toNonEmptyString(value); if (!text || !/(?:договор|contract|дог\.)/iu.test(text)) { return null; } const isoLikeMatch = text.match(/(\d{4})[-./](\d{1,2})[-./](\d{1,2})/); if (isoLikeMatch) { return normalizeDateParts(isoLikeMatch[1], isoLikeMatch[2], isoLikeMatch[3]); } const ruDateMatch = text.match(/(\d{1,2})[./-](\d{1,2})[./-](\d{4})/); if (ruDateMatch) { return normalizeDateParts(ruDateMatch[3], ruDateMatch[2], ruDateMatch[1]); } return null; } function earlierIsoDate(left: string | null, right: string | null): string | null { if (!left) { return right; } if (!right) { return left; } return right < left ? right : left; } function rowOpenSettlementContractStartDateValue(row: Record): string | null { const candidates = [ rowContractValue(row), rowDocumentValue(row), ...rowAnalyticsTextValues(row) ]; for (const candidate of candidates) { const contractDate = extractContractDateFromText(candidate); if (contractDate) { return contractDate; } } return null; } function monthBucketFromIsoDate(isoDate: string | null): string | null { const match = isoDate?.match(/^(\d{4})-(\d{2})-\d{2}$/); return match ? `${match[1]}-${match[2]}` : null; } function yearBucketFromIsoDate(isoDate: string | null): string | null { const match = isoDate?.match(/^(\d{4})-\d{2}-\d{2}$/); return match ? match[1] : null; } function netDirectionFromAmount(amount: number): AssistantMcpDiscoveryNetDirection { if (amount > 0) { return "net_incoming"; } if (amount < 0) { return "net_outgoing"; } return "balanced"; } function monthDiff(firstIsoDate: string, latestIsoDate: string): number { 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: number): string { const years = Math.floor(totalMonths / 12); const months = totalMonths % 12; const parts: string[] = []; 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: AddressMcpQueryExecutorResult | null ): AssistantMcpDiscoveryDerivedActivityPeriod | null { if (!result || result.error || result.matched_rows <= 0) { return null; } const dates = result.rows .map((row) => rowDateValue(row)) .filter((value): value is string => 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: number): string { const formatted = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 2, minimumFractionDigits: Number.isInteger(amount) ? 0 : 2 }) .format(amount) .replace(/\u00a0/g, " "); return `${formatted} руб.`; } function yearCountHumanRu(count: number): string { const abs = Math.abs(count) % 100; const last = abs % 10; const noun = abs >= 11 && abs <= 14 ? "лет" : last === 1 ? "год" : last >= 2 && last <= 4 ? "года" : "лет"; return `${count} ${noun}`; } function deriveValueFlowMonthBreakdown( result: AssistantMcpDiscoveryCoverageAwareQueryResult | null, aggregationAxis: AssistantMcpDiscoveryAggregationAxis | null ): AssistantMcpDiscoveryValueFlowMonthBucket[] { if (!result || result.error || aggregationAxis !== "month") { return []; } const buckets = new Map(); for (const row of result.rows) { const isoDate = rowDateValue(row); const monthBucket = monthBucketFromIsoDate(isoDate); const amount = rowAmountValue(row); if (!monthBucket || amount === null) { continue; } const current = buckets.get(monthBucket) ?? { rows_with_amount: 0, total_amount: 0 }; current.rows_with_amount += 1; current.total_amount += amount; buckets.set(monthBucket, current); } return Array.from(buckets.entries()) .sort(([left], [right]) => left.localeCompare(right)) .map(([monthBucket, bucket]) => ({ month_bucket: monthBucket, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount) })); } function deriveBidirectionalValueFlowMonthBreakdown(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; aggregationAxis: AssistantMcpDiscoveryAggregationAxis | null; }): AssistantMcpDiscoveryBidirectionalValueFlowMonthBucket[] { if (input.aggregationAxis !== "month") { return []; } const incomingBuckets = deriveValueFlowMonthBreakdown(input.incomingResult, "month"); const outgoingBuckets = deriveValueFlowMonthBreakdown(input.outgoingResult, "month"); const allMonthBuckets = new Set(); for (const bucket of incomingBuckets) { allMonthBuckets.add(bucket.month_bucket); } for (const bucket of outgoingBuckets) { allMonthBuckets.add(bucket.month_bucket); } const incomingByMonth = new Map(incomingBuckets.map((bucket) => [bucket.month_bucket, bucket])); const outgoingByMonth = new Map(outgoingBuckets.map((bucket) => [bucket.month_bucket, bucket])); return Array.from(allMonthBuckets) .sort((left, right) => left.localeCompare(right)) .map((monthBucket) => { const incoming = incomingByMonth.get(monthBucket); const outgoing = outgoingByMonth.get(monthBucket); const incomingAmount = incoming?.total_amount ?? 0; const outgoingAmount = outgoing?.total_amount ?? 0; const netAmount = incomingAmount - outgoingAmount; return { month_bucket: monthBucket, incoming_total_amount: incomingAmount, incoming_total_amount_human_ru: formatAmountHumanRu(incomingAmount), incoming_rows_with_amount: incoming?.rows_with_amount ?? 0, outgoing_total_amount: outgoingAmount, outgoing_total_amount_human_ru: formatAmountHumanRu(outgoingAmount), outgoing_rows_with_amount: outgoing?.rows_with_amount ?? 0, net_amount: netAmount, net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)), net_direction: netDirectionFromAmount(netAmount) }; }); } function deriveBusinessOverviewSideYearBreakdown( result: AssistantMcpDiscoveryCoverageAwareQueryResult | null ): Array<{ year_bucket: string; rows_with_amount: number; total_amount: number; total_amount_human_ru: string }> { if (!result || result.error) { return []; } const buckets = new Map(); for (const row of result.rows) { const yearBucket = yearBucketFromIsoDate(rowDateValue(row)); const amount = rowAmountValue(row); if (!yearBucket || amount === null) { continue; } const current = buckets.get(yearBucket) ?? { rows_with_amount: 0, total_amount: 0 }; current.rows_with_amount += 1; current.total_amount += amount; buckets.set(yearBucket, current); } return Array.from(buckets.entries()) .sort(([left], [right]) => left.localeCompare(right)) .map(([yearBucket, bucket]) => ({ year_bucket: yearBucket, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount) })); } function deriveBusinessOverviewYearlyBreakdown(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; }): AssistantMcpDiscoveryBusinessOverviewYearBucket[] { const incomingBuckets = deriveBusinessOverviewSideYearBreakdown(input.incomingResult); const outgoingBuckets = deriveBusinessOverviewSideYearBreakdown(input.outgoingResult); const allYearBuckets = new Set(); for (const bucket of incomingBuckets) { allYearBuckets.add(bucket.year_bucket); } for (const bucket of outgoingBuckets) { allYearBuckets.add(bucket.year_bucket); } const incomingByYear = new Map(incomingBuckets.map((bucket) => [bucket.year_bucket, bucket])); const outgoingByYear = new Map(outgoingBuckets.map((bucket) => [bucket.year_bucket, bucket])); return Array.from(allYearBuckets) .sort((left, right) => left.localeCompare(right)) .map((yearBucket) => { const incoming = incomingByYear.get(yearBucket); const outgoing = outgoingByYear.get(yearBucket); const incomingAmount = incoming?.total_amount ?? 0; const outgoingAmount = outgoing?.total_amount ?? 0; const netAmount = incomingAmount - outgoingAmount; return { year_bucket: yearBucket, incoming_total_amount: incomingAmount, incoming_total_amount_human_ru: formatAmountHumanRu(incomingAmount), incoming_rows_with_amount: incoming?.rows_with_amount ?? 0, outgoing_total_amount: outgoingAmount, outgoing_total_amount_human_ru: formatAmountHumanRu(outgoingAmount), outgoing_rows_with_amount: outgoing?.rows_with_amount ?? 0, net_amount: netAmount, net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)), net_direction: netDirectionFromAmount(netAmount) }; }); } function deriveValueFlow( result: AssistantMcpDiscoveryCoverageAwareQueryResult | null, counterparty: string | null, periodScope: string | null, direction: AssistantMcpDiscoveryDerivedValueFlow["value_flow_direction"], aggregationAxis: AssistantMcpDiscoveryAggregationAxis | null ): AssistantMcpDiscoveryDerivedValueFlow | null { 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): value is string => Boolean(value)) .sort(); return { value_flow_direction: direction, counterparty, period_scope: periodScope, aggregation_axis: aggregationAxis, 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, coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit, coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking, period_chunking_granularity: result.period_chunking_granularity, monthly_breakdown: deriveValueFlowMonthBreakdown(result, aggregationAxis), inference_basis: "sum_of_confirmed_1c_value_flow_rows" }; } function deriveRankedValueFlow( result: AssistantMcpDiscoveryCoverageAwareQueryResult | null, input: { organizationScope: string | null; periodScope: string | null; direction: AssistantMcpDiscoveryDerivedRankedValueFlow["value_flow_direction"]; rankingNeed: AssistantMcpDiscoveryDerivedRankedValueFlow["ranking_need"]; } ): AssistantMcpDiscoveryDerivedRankedValueFlow | null { if (!result || result.error || result.matched_rows <= 0) { return null; } const buckets = new Map(); let rowsWithAmount = 0; for (const row of result.rows) { const axisValue = rowCounterpartyValue(row); const amount = rowAmountValue(row); if (!axisValue || amount === null) { continue; } rowsWithAmount += 1; const current = buckets.get(axisValue) ?? { rows_with_amount: 0, total_amount: 0 }; current.rows_with_amount += 1; current.total_amount += amount; buckets.set(axisValue, current); } if (rowsWithAmount <= 0 || buckets.size <= 0) { return null; } const rankedValues = Array.from(buckets.entries()) .map(([axisValue, bucket]) => ({ axis_value: axisValue, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount) })) .sort((left, right) => { const amountDelta = right.total_amount - left.total_amount; if (input.rankingNeed === "bottom_asc") { if (amountDelta !== 0) { return -amountDelta; } } else if (amountDelta !== 0) { return amountDelta; } return left.axis_value.localeCompare(right.axis_value, "ru"); }) .slice(0, 5); return { value_flow_direction: input.direction, ranking_need: input.rankingNeed, ranking_axis: "counterparty", organization_scope: input.organizationScope, period_scope: input.periodScope, rows_matched: result.matched_rows, rows_with_amount: rowsWithAmount, ranked_values: rankedValues, coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit, coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking, period_chunking_granularity: result.period_chunking_granularity, inference_basis: "ranked_counterparty_totals_from_confirmed_1c_value_flow_rows" }; } function deriveValueFlowSideSummary( result: AssistantMcpDiscoveryCoverageAwareQueryResult | null ): AssistantMcpDiscoveryValueFlowSideSummary { if (!result || result.error || result.matched_rows <= 0) { return { rows_matched: 0, rows_with_amount: 0, total_amount: 0, total_amount_human_ru: formatAmountHumanRu(0), first_movement_date: null, latest_movement_date: null, coverage_limited_by_probe_limit: false, coverage_recovered_by_period_chunking: false, period_chunking_granularity: null }; } let totalAmount = 0; let rowsWithAmount = 0; for (const row of result.rows) { const amount = rowAmountValue(row); if (amount !== null) { totalAmount += amount; rowsWithAmount += 1; } } const dates = result.rows .map((row) => rowDateValue(row)) .filter((value): value is string => Boolean(value)) .sort(); return { 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, coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit, coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking, period_chunking_granularity: result.period_chunking_granularity }; } function deriveBidirectionalValueFlow(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; counterparty: string | null; periodScope: string | null; aggregationAxis: AssistantMcpDiscoveryAggregationAxis | null; }): AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null { const incoming = deriveValueFlowSideSummary(input.incomingResult); const outgoing = deriveValueFlowSideSummary(input.outgoingResult); if (incoming.rows_with_amount <= 0 && outgoing.rows_with_amount <= 0) { return null; } const netAmount = incoming.total_amount - outgoing.total_amount; return { counterparty: input.counterparty, period_scope: input.periodScope, aggregation_axis: input.aggregationAxis, incoming_customer_revenue: incoming, outgoing_supplier_payout: outgoing, net_amount: netAmount, net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)), net_direction: netDirectionFromAmount(netAmount), coverage_limited_by_probe_limit: incoming.coverage_limited_by_probe_limit || outgoing.coverage_limited_by_probe_limit, coverage_recovered_by_period_chunking: incoming.coverage_recovered_by_period_chunking || outgoing.coverage_recovered_by_period_chunking, period_chunking_granularity: incoming.period_chunking_granularity ?? outgoing.period_chunking_granularity ?? null, monthly_breakdown: deriveBidirectionalValueFlowMonthBreakdown({ incomingResult: input.incomingResult, outgoingResult: input.outgoingResult, aggregationAxis: input.aggregationAxis }), inference_basis: "incoming_minus_outgoing_confirmed_1c_value_flow_rows" }; } function summarizeBidirectionalValueFlowRows(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; }): string | null { const incoming = input.incomingResult; const outgoing = input.outgoingResult; if (!incoming && !outgoing) { return null; } const incomingSummary = incoming?.error ? "incoming value-flow query failed" : incoming?.coverage_recovered_by_period_chunking && incoming.period_chunking_granularity === "month" ? `${incoming.period_chunk_count} monthly incoming value-flow probes fetched ${incoming.fetched_rows} rows total, ${incoming.matched_rows} matched` : `${incoming?.fetched_rows ?? 0} incoming value-flow rows fetched, ${incoming?.matched_rows ?? 0} matched`; const outgoingSummary = outgoing?.error ? "outgoing supplier-payout query failed" : outgoing?.coverage_recovered_by_period_chunking && outgoing.period_chunking_granularity === "month" ? `${outgoing.period_chunk_count} monthly outgoing supplier-payout probes fetched ${outgoing.fetched_rows} rows total, ${outgoing.matched_rows} matched` : `${outgoing?.fetched_rows ?? 0} outgoing supplier-payout rows fetched, ${outgoing?.matched_rows ?? 0} matched`; return `${incomingSummary}; ${outgoingSummary}`; } function deriveBusinessOverviewTaxPosition( result: AddressMcpQueryExecutorResult | null, periodScope: string | null ): AssistantMcpDiscoveryDerivedBusinessOverviewTaxPosition | null { if (!result || result.error || result.matched_rows <= 0 || !periodScope) { return null; } let salesVatAmount = 0; let purchaseVatAmount = 0; let rowsWithAmount = 0; for (const row of result.rows) { const amount = rowAmountValue(row); if (amount === null) { continue; } const marker = String(rowDocumentValue(row) ?? "").toLowerCase(); const account = String(rowAccountValue(row) ?? "").toLowerCase(); if (marker.includes("sales") || marker.includes("продаж") || account.startsWith("68")) { salesVatAmount += amount; rowsWithAmount += 1; continue; } if (marker.includes("purchase") || marker.includes("покуп") || account.startsWith("19")) { purchaseVatAmount += amount; rowsWithAmount += 1; } } if (rowsWithAmount <= 0) { return null; } const netVatAmount = salesVatAmount - purchaseVatAmount; const netVatDirection = netVatAmount > 0 ? "vat_to_pay" : netVatAmount < 0 ? "vat_to_recover_or_offset" : "balanced"; return { period_scope: periodScope, rows_matched: result.matched_rows, rows_with_amount: rowsWithAmount, sales_vat_amount: salesVatAmount, sales_vat_amount_human_ru: formatAmountHumanRu(salesVatAmount), purchase_vat_amount: purchaseVatAmount, purchase_vat_amount_human_ru: formatAmountHumanRu(purchaseVatAmount), net_vat_amount: netVatAmount, net_vat_amount_human_ru: formatAmountHumanRu(Math.abs(netVatAmount)), net_vat_direction: netVatDirection, inference_basis: "sales_book_minus_purchase_book_confirmed_1c_vat_rows" }; } function deriveBusinessOverviewTradingMarginProxy( result: AddressMcpQueryExecutorResult | null, periodScope: string | null ): AssistantMcpDiscoveryDerivedBusinessOverviewTradingMarginProxy | null { if (!result || result.error || result.matched_rows <= 0 || !periodScope) { return null; } const itemBuckets = new Map(); let salesRowsWithAmount = 0; let purchaseRowsWithAmount = 0; let salesRevenue = 0; let purchaseCostProxy = 0; for (const row of result.rows) { const amount = rowAmountValue(row); if (amount === null || amount <= 0) { continue; } const isSale = accountTextMatchesPrefix(rowCreditAccountValue(row), ["41.01"]); const isPurchase = accountTextMatchesPrefix(rowDebitAccountValue(row), ["41.01"]); if (!isSale && !isPurchase) { continue; } const item = rowInventoryItemValue(row) ?? "unknown_item"; const quantity = rowQuantityValue(row) ?? 0; const bucket = itemBuckets.get(item) ?? { sales_revenue: 0, purchase_cost_proxy: 0, sales_quantity: 0, purchase_quantity: 0 }; if (isSale) { salesRowsWithAmount += 1; salesRevenue += amount; bucket.sales_revenue += amount; bucket.sales_quantity += quantity; } if (isPurchase) { purchaseRowsWithAmount += 1; purchaseCostProxy += amount; bucket.purchase_cost_proxy += amount; bucket.purchase_quantity += quantity; } itemBuckets.set(item, bucket); } if (salesRowsWithAmount <= 0 && purchaseRowsWithAmount <= 0) { return null; } const grossSpreadProxy = salesRevenue - purchaseCostProxy; const marginToRevenuePct = salesRevenue > 0 ? percentageOfTotal(grossSpreadProxy, salesRevenue) : null; const markupToPurchasePct = purchaseCostProxy > 0 ? percentageOfTotal(grossSpreadProxy, purchaseCostProxy) : null; const topItemsBySales = Array.from(itemBuckets.entries()) .map(([item, bucket]) => ({ item, sales_revenue: bucket.sales_revenue, sales_revenue_human_ru: formatAmountHumanRu(bucket.sales_revenue), purchase_cost_proxy: bucket.purchase_cost_proxy, purchase_cost_proxy_human_ru: formatAmountHumanRu(bucket.purchase_cost_proxy), gross_spread_proxy: bucket.sales_revenue - bucket.purchase_cost_proxy, gross_spread_proxy_human_ru: formatAmountHumanRu(bucket.sales_revenue - bucket.purchase_cost_proxy), sales_quantity: bucket.sales_quantity, purchase_quantity: bucket.purchase_quantity })) .sort((left, right) => { const salesDelta = right.sales_revenue - left.sales_revenue; return salesDelta !== 0 ? salesDelta : left.item.localeCompare(right.item, "ru"); }) .slice(0, 5); return { period_scope: periodScope, rows_matched: result.matched_rows, sales_rows_with_amount: salesRowsWithAmount, purchase_rows_with_amount: purchaseRowsWithAmount, sales_revenue: salesRevenue, sales_revenue_human_ru: formatAmountHumanRu(salesRevenue), purchase_cost_proxy: purchaseCostProxy, purchase_cost_proxy_human_ru: formatAmountHumanRu(purchaseCostProxy), gross_spread_proxy: grossSpreadProxy, gross_spread_proxy_human_ru: formatAmountHumanRu(grossSpreadProxy), margin_to_revenue_pct: marginToRevenuePct, markup_to_purchase_pct: markupToPurchasePct, top_items_by_sales: topItemsBySales, inference_basis: "sales_documents_minus_purchase_documents_confirmed_1c_rows" }; } function deriveBusinessOverviewDebtSide( result: AddressMcpQueryExecutorResult | null ): AssistantMcpDiscoveryBusinessOverviewDebtSideSummary { if (!result || result.error || result.matched_rows <= 0) { return { rows_matched: 0, rows_with_amount: 0, total_amount: 0, total_amount_human_ru: formatAmountHumanRu(0), top_counterparties: [] }; } const buckets = new Map(); let rowsWithAmount = 0; let totalAmount = 0; for (const row of result.rows) { const amount = rowAmountValue(row); if (amount === null) { continue; } rowsWithAmount += 1; totalAmount += amount; const counterparty = rowCounterpartyValue(row) ?? "unknown_counterparty"; const current = buckets.get(counterparty) ?? { rows_with_amount: 0, total_amount: 0 }; current.rows_with_amount += 1; current.total_amount += amount; buckets.set(counterparty, current); } const topCounterparties = Array.from(buckets.entries()) .map(([axisValue, bucket]) => ({ axis_value: axisValue, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount) })) .sort((left, right) => { const amountDelta = right.total_amount - left.total_amount; return amountDelta !== 0 ? amountDelta : left.axis_value.localeCompare(right.axis_value, "ru"); }) .slice(0, 5); return { rows_matched: result.matched_rows, rows_with_amount: rowsWithAmount, total_amount: totalAmount, total_amount_human_ru: formatAmountHumanRu(totalAmount), top_counterparties: topCounterparties }; } function deriveBusinessOverviewDebtPosition(input: { receivablesResult: AddressMcpQueryExecutorResult | null; payablesResult: AddressMcpQueryExecutorResult | null; debtAsOfDate: string | null; }): AssistantMcpDiscoveryDerivedBusinessOverviewDebtPosition | null { if (!input.debtAsOfDate) { return null; } const receivables = deriveBusinessOverviewDebtSide(input.receivablesResult); const payables = deriveBusinessOverviewDebtSide(input.payablesResult); if (receivables.rows_with_amount <= 0 && payables.rows_with_amount <= 0) { return null; } const netDebtPositionAmount = receivables.total_amount - payables.total_amount; const netDebtPositionDirection = netDebtPositionAmount > 0 ? "net_receivable" : netDebtPositionAmount < 0 ? "net_payable" : "balanced"; return { as_of_date: input.debtAsOfDate, receivables, payables, net_debt_position_amount: netDebtPositionAmount, net_debt_position_amount_human_ru: formatAmountHumanRu(Math.abs(netDebtPositionAmount)), net_debt_position_direction: netDebtPositionDirection, inference_basis: "receivables_minus_payables_confirmed_1c_balance_rows" }; } function percentageOfTotal(part: number, total: number): number | null { if (!Number.isFinite(part) || !Number.isFinite(total) || total <= 0) { return null; } return Math.round((part / total) * 10_000) / 100; } function ratioOfTotal(part: number, total: number): number | null { if (!Number.isFinite(part) || !Number.isFinite(total) || total <= 0) { return null; } return Math.round((part / total) * 100) / 100; } function deriveBusinessOverviewDebtOpenSettlementQuality(input: { openContractsResult: AddressMcpQueryExecutorResult | null; debtAsOfDate: string | null; }): AssistantMcpDiscoveryDerivedBusinessOverviewDebtOpenSettlementQuality | null { if (!input.debtAsOfDate || !input.openContractsResult || input.openContractsResult.error || input.openContractsResult.matched_rows <= 0) { return null; } const debtAsOfDate = input.debtAsOfDate; const counterpartyBuckets = new Map(); const contractBuckets = new Map< string, { counterparty: string | null; contract_start_date: string | null; rows_with_amount: number; total_amount: number } >(); const counterparties = new Set(); const contracts = new Set(); let rowsWithAmount = 0; let grossOpenAmount = 0; let rowsWithoutCounterparty = 0; let rowsWithoutContract = 0; for (const row of input.openContractsResult.rows) { const amount = rowAmountValue(row); if (amount === null) { continue; } const absAmount = Math.abs(amount); if (absAmount <= 0) { continue; } rowsWithAmount += 1; grossOpenAmount += absAmount; const counterparty = rowCounterpartyValue(row); const contract = rowContractValue(row); if (counterparty) { counterparties.add(counterparty); const current = counterpartyBuckets.get(counterparty) ?? { rows_with_amount: 0, total_amount: 0 }; current.rows_with_amount += 1; current.total_amount += absAmount; counterpartyBuckets.set(counterparty, current); } else { rowsWithoutCounterparty += 1; } if (contract) { const contractStartDate = rowOpenSettlementContractStartDateValue(row); contracts.add(contract); const current = contractBuckets.get(contract) ?? { counterparty, contract_start_date: contractStartDate, rows_with_amount: 0, total_amount: 0 }; if (!current.counterparty && counterparty) { current.counterparty = counterparty; } current.contract_start_date = earlierIsoDate(current.contract_start_date, contractStartDate); current.rows_with_amount += 1; current.total_amount += absAmount; contractBuckets.set(contract, current); } else { rowsWithoutContract += 1; } } if (rowsWithAmount <= 0) { return null; } const topCounterparties = Array.from(counterpartyBuckets.entries()) .map(([axisValue, bucket]) => ({ axis_value: axisValue, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount) })) .sort((left, right) => { const amountDelta = right.total_amount - left.total_amount; return amountDelta !== 0 ? amountDelta : left.axis_value.localeCompare(right.axis_value, "ru"); }) .slice(0, 5); const topContracts = Array.from(contractBuckets.entries()) .map(([contract, bucket]) => ({ contract, counterparty: bucket.counterparty, contract_start_date: bucket.contract_start_date, rows_with_amount: bucket.rows_with_amount, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount), share_of_gross_open_amount_pct: percentageOfTotal(bucket.total_amount, grossOpenAmount) })) .sort((left, right) => { const amountDelta = right.total_amount - left.total_amount; return amountDelta !== 0 ? amountDelta : left.contract.localeCompare(right.contract, "ru"); }) .slice(0, 5); const agedContractBuckets = Array.from(contractBuckets.entries()) .map(([contract, bucket]): AssistantMcpDiscoveryBusinessOverviewDebtAgeContractBucket | null => { if (!bucket.contract_start_date) { return null; } return { contract, counterparty: bucket.counterparty, start_date: bucket.contract_start_date, age_days: daysBetweenIsoDates(bucket.contract_start_date, debtAsOfDate), total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount), share_of_gross_open_amount_pct: percentageOfTotal(bucket.total_amount, grossOpenAmount) }; }) .filter((item): item is AssistantMcpDiscoveryBusinessOverviewDebtAgeContractBucket => Boolean(item)) .sort((left, right) => { const leftAge = left.age_days ?? -1; const rightAge = right.age_days ?? -1; const ageDelta = rightAge - leftAge; if (ageDelta !== 0) { return ageDelta; } const amountDelta = right.total_amount - left.total_amount; return amountDelta !== 0 ? amountDelta : left.contract.localeCompare(right.contract, "ru"); }); const debtAgeDates = agedContractBuckets.map((bucket) => bucket.start_date).sort(); const maxAgeDays = agedContractBuckets.reduce((max, bucket) => { if (bucket.age_days === null) { return max; } return max === null ? bucket.age_days : Math.max(max, bucket.age_days); }, null); const ageSignal: AssistantMcpDiscoveryBusinessOverviewDebtAgeSignal | null = agedContractBuckets.length > 0 ? { contracts_with_start_date: agedContractBuckets.length, oldest_start_date: debtAgeDates[0] ?? null, latest_start_date: debtAgeDates[debtAgeDates.length - 1] ?? null, max_age_days: maxAgeDays, top_aged_contracts: agedContractBuckets.slice(0, 5), inference_basis: "contract_dates_from_open_settlement_rows" } : null; return { as_of_date: debtAsOfDate, rows_matched: input.openContractsResult.matched_rows, rows_with_amount: rowsWithAmount, gross_open_amount: grossOpenAmount, gross_open_amount_human_ru: formatAmountHumanRu(grossOpenAmount), unique_counterparties: counterparties.size, unique_contracts: contracts.size, rows_without_counterparty: rowsWithoutCounterparty, rows_without_contract: rowsWithoutContract, top_counterparties: topCounterparties, top_contracts: topContracts, concentration_top_counterparty_pct: percentageOfTotal(topCounterparties[0]?.total_amount ?? 0, grossOpenAmount), concentration_top_contract_pct: percentageOfTotal(topContracts[0]?.total_amount ?? 0, grossOpenAmount), age_signal: ageSignal, inference_basis: "open_contracts_confirmed_1c_balance_rows" }; } function debtStalenessRiskBand(input: { maxContractAgeDays: number; topContractSharePct: number; }): AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy["risk_band"] { if (input.maxContractAgeDays >= 365 && input.topContractSharePct >= 50) { return "high"; } if (input.maxContractAgeDays >= 365 || input.topContractSharePct >= 50) { return "elevated"; } if (input.maxContractAgeDays >= 180 || input.topContractSharePct >= 35) { return "watch"; } return "lower_visible_risk"; } function deriveBusinessOverviewDebtStalenessRiskProxy( quality: AssistantMcpDiscoveryDerivedBusinessOverviewDebtOpenSettlementQuality | null ): AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy | null { const ageSignal = quality?.age_signal; const topAgedContract = ageSignal?.top_aged_contracts[0]; const topContractSharePct = topAgedContract?.share_of_gross_open_amount_pct; if ( !quality || !ageSignal?.oldest_start_date || ageSignal.max_age_days === null || ageSignal.max_age_days === undefined || !topAgedContract || topContractSharePct === null || topContractSharePct === undefined ) { return null; } return { as_of_date: quality.as_of_date, gross_open_amount: quality.gross_open_amount, gross_open_amount_human_ru: quality.gross_open_amount_human_ru, oldest_contract_start_date: ageSignal.oldest_start_date, max_contract_age_days: ageSignal.max_age_days, top_contract: topAgedContract.contract, top_contract_counterparty: topAgedContract.counterparty, top_contract_amount: topAgedContract.total_amount, top_contract_amount_human_ru: topAgedContract.total_amount_human_ru, top_contract_share_pct: topContractSharePct, risk_band: debtStalenessRiskBand({ maxContractAgeDays: ageSignal.max_age_days, topContractSharePct }), inference_basis: "contract_date_age_and_open_balance_concentration_confirmed_1c_rows" }; } function debtStalenessRiskBandRu( riskBand: AssistantMcpDiscoveryDerivedBusinessOverviewDebtStalenessRiskProxy["risk_band"] ): string { if (riskBand === "high") { return "высокая зона внимания"; } if (riskBand === "elevated") { return "повышенная зона внимания"; } if (riskBand === "watch") { return "зона наблюдения"; } return "низкий видимый риск"; } function daysBetweenIsoDates(leftIsoDate: string, rightIsoDate: string): number | null { const leftMatch = leftIsoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/); const rightMatch = rightIsoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/); if (!leftMatch || !rightMatch) { return null; } const left = Date.UTC(Number(leftMatch[1]), Number(leftMatch[2]) - 1, Number(leftMatch[3])); const right = Date.UTC(Number(rightMatch[1]), Number(rightMatch[2]) - 1, Number(rightMatch[3])); if (!Number.isFinite(left) || !Number.isFinite(right)) { return null; } return Math.max(0, Math.floor((right - left) / 86_400_000)); } function deriveBusinessOverviewInventoryAgingSignal( result: AddressMcpQueryExecutorResult | null, inventoryAsOfDate: string ): AssistantMcpDiscoveryBusinessOverviewInventoryAgingSignal | null { if (!result || result.error || result.matched_rows <= 0) { return null; } const dates = result.rows .map((row) => rowDateValue(row)) .filter((value): value is string => Boolean(value)) .sort((left, right) => left.localeCompare(right)); if (dates.length <= 0) { return null; } const oldestPurchaseDate = dates[0] ?? null; const latestPurchaseDate = dates[dates.length - 1] ?? null; return { rows_matched: result.matched_rows, rows_with_purchase_date: dates.length, oldest_purchase_date: oldestPurchaseDate, latest_purchase_date: latestPurchaseDate, max_age_days: oldestPurchaseDate ? daysBetweenIsoDates(oldestPurchaseDate, inventoryAsOfDate) : null, inference_basis: "inventory_purchase_dates_from_confirmed_1c_rows" }; } function deriveBusinessOverviewInventoryPosition(input: { inventoryOnHandResult: AddressMcpQueryExecutorResult | null; inventoryAgingResult: AddressMcpQueryExecutorResult | null; inventoryAsOfDate: string | null; }): AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null { const { inventoryAsOfDate, inventoryOnHandResult } = input; if (!inventoryAsOfDate || !inventoryOnHandResult || inventoryOnHandResult.error || inventoryOnHandResult.matched_rows <= 0) { return null; } const buckets = new Map(); let rowsWithAmount = 0; let rowsWithQuantity = 0; let totalAmount = 0; let totalQuantity = 0; for (const row of inventoryOnHandResult.rows) { const amount = rowAmountValue(row); const quantity = rowQuantityValue(row); if (amount === null && quantity === null) { continue; } const item = rowInventoryItemValue(row) ?? "unknown_item"; const current = buckets.get(item) ?? { rows_with_amount: 0, rows_with_quantity: 0, total_amount: 0, total_quantity: 0 }; if (amount !== null) { rowsWithAmount += 1; totalAmount += amount; current.rows_with_amount += 1; current.total_amount += amount; } if (quantity !== null) { rowsWithQuantity += 1; totalQuantity += quantity; current.rows_with_quantity += 1; current.total_quantity += quantity; } buckets.set(item, current); } if (rowsWithAmount <= 0 && rowsWithQuantity <= 0) { return null; } const topItems = Array.from(buckets.entries()) .map(([item, bucket]) => ({ item, rows_with_amount: bucket.rows_with_amount, rows_with_quantity: bucket.rows_with_quantity, total_amount: bucket.total_amount, total_amount_human_ru: formatAmountHumanRu(bucket.total_amount), total_quantity: bucket.total_quantity })) .sort((left, right) => { const amountDelta = right.total_amount - left.total_amount; if (amountDelta !== 0) { return amountDelta; } const quantityDelta = right.total_quantity - left.total_quantity; return quantityDelta !== 0 ? quantityDelta : left.item.localeCompare(right.item, "ru"); }) .slice(0, 5); return { as_of_date: inventoryAsOfDate, rows_matched: inventoryOnHandResult.matched_rows, rows_with_amount: rowsWithAmount, rows_with_quantity: rowsWithQuantity, total_amount: totalAmount, total_amount_human_ru: formatAmountHumanRu(totalAmount), total_quantity: totalQuantity, top_items: topItems, aging_signal: deriveBusinessOverviewInventoryAgingSignal(input.inventoryAgingResult, inventoryAsOfDate), inference_basis: "inventory_on_hand_confirmed_1c_balance_rows" }; } function deriveBusinessOverviewInventoryTurnoverProxy(input: { inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; tradingMarginProxy: AssistantMcpDiscoveryDerivedBusinessOverviewTradingMarginProxy | null; }): AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null { const { inventoryPosition, tradingMarginProxy } = input; if (!inventoryPosition || !tradingMarginProxy) { return null; } if (inventoryPosition.total_amount <= 0 || tradingMarginProxy.sales_revenue <= 0) { return null; } return { period_scope: tradingMarginProxy.period_scope, as_of_date: inventoryPosition.as_of_date, sales_revenue: tradingMarginProxy.sales_revenue, sales_revenue_human_ru: tradingMarginProxy.sales_revenue_human_ru, inventory_amount: inventoryPosition.total_amount, inventory_amount_human_ru: inventoryPosition.total_amount_human_ru, sales_to_stock_amount_ratio: ratioOfTotal(tradingMarginProxy.sales_revenue, inventoryPosition.total_amount), stock_to_sales_revenue_pct: percentageOfTotal(inventoryPosition.total_amount, tradingMarginProxy.sales_revenue), inference_basis: "sales_document_revenue_vs_inventory_balance_confirmed_1c_rows" }; } function inventoryStalenessRiskBand(input: { maxPurchaseAgeDays: number; salesToStockAmountRatio: number; }): AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy["risk_band"] { if (input.maxPurchaseAgeDays >= 365 && input.salesToStockAmountRatio < 1) { return "high"; } if (input.maxPurchaseAgeDays >= 365 || input.salesToStockAmountRatio < 1) { return "elevated"; } if (input.maxPurchaseAgeDays >= 180 || input.salesToStockAmountRatio < 2) { return "watch"; } return "lower_visible_risk"; } function deriveBusinessOverviewInventoryStalenessRiskProxy(input: { inventoryPosition: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryPosition | null; inventoryTurnoverProxy: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryTurnoverProxy | null; }): AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy | null { const { inventoryPosition, inventoryTurnoverProxy } = input; const maxPurchaseAgeDays = inventoryPosition?.aging_signal?.max_age_days; const oldestPurchaseDate = inventoryPosition?.aging_signal?.oldest_purchase_date; const salesToStockAmountRatio = inventoryTurnoverProxy?.sales_to_stock_amount_ratio; if ( !inventoryPosition || !inventoryTurnoverProxy || !oldestPurchaseDate || maxPurchaseAgeDays === null || maxPurchaseAgeDays === undefined || salesToStockAmountRatio === null || salesToStockAmountRatio === undefined ) { return null; } return { as_of_date: inventoryPosition.as_of_date, period_scope: inventoryTurnoverProxy.period_scope, oldest_purchase_date: oldestPurchaseDate, max_purchase_age_days: maxPurchaseAgeDays, sales_to_stock_amount_ratio: salesToStockAmountRatio, risk_band: inventoryStalenessRiskBand({ maxPurchaseAgeDays, salesToStockAmountRatio }), inference_basis: "purchase_date_age_and_sales_to_stock_proxy_confirmed_1c_rows" }; } function inventoryStalenessRiskBandRu( riskBand: AssistantMcpDiscoveryDerivedBusinessOverviewInventoryStalenessRiskProxy["risk_band"] ): string { if (riskBand === "high") { return "высокая зона внимания"; } if (riskBand === "elevated") { return "повышенная зона внимания"; } if (riskBand === "watch") { return "зона наблюдения"; } return "низкий видимый риск"; } function deriveBusinessOverview(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; lifecycleResult: AddressMcpQueryExecutorResult | null; taxResult: AddressMcpQueryExecutorResult | null; tradingMarginResult: AddressMcpQueryExecutorResult | null; receivablesResult: AddressMcpQueryExecutorResult | null; payablesResult: AddressMcpQueryExecutorResult | null; openContractsResult: AddressMcpQueryExecutorResult | null; debtAsOfDate: string | null; inventoryOnHandResult: AddressMcpQueryExecutorResult | null; inventoryAgingResult: AddressMcpQueryExecutorResult | null; inventoryAsOfDate: string | null; organizationScope: string | null; periodScope: string | null; }): AssistantMcpDiscoveryDerivedBusinessOverview | null { const incoming = deriveValueFlowSideSummary(input.incomingResult); const outgoing = deriveValueFlowSideSummary(input.outgoingResult); const rankedIncoming = deriveRankedValueFlow(input.incomingResult, { organizationScope: input.organizationScope, periodScope: input.periodScope, direction: "incoming_customer_revenue", rankingNeed: "top_desc" }); const rankedOutgoing = deriveRankedValueFlow(input.outgoingResult, { organizationScope: input.organizationScope, periodScope: input.periodScope, direction: "outgoing_supplier_payout", rankingNeed: "top_desc" }); const yearlyBreakdown = deriveBusinessOverviewYearlyBreakdown({ incomingResult: input.incomingResult, outgoingResult: input.outgoingResult }); const activityPeriod = deriveActivityPeriod(input.lifecycleResult); const taxPosition = deriveBusinessOverviewTaxPosition(input.taxResult, input.periodScope); const tradingMarginProxy = deriveBusinessOverviewTradingMarginProxy(input.tradingMarginResult, input.periodScope); const debtPosition = deriveBusinessOverviewDebtPosition({ receivablesResult: input.receivablesResult, payablesResult: input.payablesResult, debtAsOfDate: input.debtAsOfDate }); const debtOpenSettlementQuality = deriveBusinessOverviewDebtOpenSettlementQuality({ openContractsResult: input.openContractsResult, debtAsOfDate: input.debtAsOfDate }); const debtStalenessRiskProxy = deriveBusinessOverviewDebtStalenessRiskProxy(debtOpenSettlementQuality); const inventoryPosition = deriveBusinessOverviewInventoryPosition({ inventoryOnHandResult: input.inventoryOnHandResult, inventoryAgingResult: input.inventoryAgingResult, inventoryAsOfDate: input.inventoryAsOfDate }); const inventoryTurnoverProxy = deriveBusinessOverviewInventoryTurnoverProxy({ inventoryPosition, tradingMarginProxy }); const inventoryStalenessRiskProxy = deriveBusinessOverviewInventoryStalenessRiskProxy({ inventoryPosition, inventoryTurnoverProxy }); const checkedSignalCount = [ incoming.rows_with_amount > 0, outgoing.rows_with_amount > 0, Boolean(activityPeriod), Boolean(taxPosition), Boolean(tradingMarginProxy), Boolean(debtPosition), Boolean(debtOpenSettlementQuality), Boolean(debtStalenessRiskProxy), Boolean(inventoryPosition), Boolean(inventoryTurnoverProxy), Boolean(inventoryStalenessRiskProxy) ].filter(Boolean).length; if (checkedSignalCount <= 0) { return null; } const netAmount = incoming.total_amount - outgoing.total_amount; return { organization_scope: input.organizationScope, period_scope: input.periodScope, incoming_customer_revenue: incoming, outgoing_supplier_payout: outgoing, net_amount: netAmount, net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)), net_direction: netDirectionFromAmount(netAmount), top_customers: rankedIncoming?.ranked_values ?? [], top_suppliers: rankedOutgoing?.ranked_values ?? [], yearly_breakdown: yearlyBreakdown, activity_period: activityPeriod, tax_position: taxPosition, trading_margin_proxy: tradingMarginProxy, debt_position: debtPosition, debt_open_settlement_quality: debtOpenSettlementQuality, debt_staleness_risk_proxy: debtStalenessRiskProxy, inventory_position: inventoryPosition, inventory_turnover_proxy: inventoryTurnoverProxy, inventory_staleness_risk_proxy: inventoryStalenessRiskProxy, coverage_limited_by_probe_limit: incoming.coverage_limited_by_probe_limit || outgoing.coverage_limited_by_probe_limit, checked_signal_count: checkedSignalCount, missing_signal_families: [ tradingMarginProxy ? "accounting_profit_margin" : "profit_margin", debtPosition ? null : "debt_position", debtOpenSettlementQuality ? "debt_due_date_aging_quality" : "debt_open_settlement_quality", taxPosition ? null : "tax_position", inventoryPosition ? inventoryStalenessRiskProxy ? "inventory_reserve_liquidation_quality" : inventoryTurnoverProxy ? "inventory_liquidity_quality" : "inventory_turnover_quality" : "inventory_position", inventoryPosition?.aging_signal ? null : "inventory_aging_quality" ].filter((item): item is string => Boolean(item)), inference_basis: inventoryPosition ? "business_overview_from_confirmed_1c_multi_family_rows" : debtOpenSettlementQuality ? "business_overview_from_confirmed_1c_multi_family_rows" : taxPosition && debtPosition ? "business_overview_from_confirmed_1c_money_activity_tax_and_debt_rows" : taxPosition ? "business_overview_from_confirmed_1c_money_activity_and_tax_rows" : debtPosition ? "business_overview_from_confirmed_1c_money_activity_and_debt_rows" : "business_overview_from_confirmed_1c_money_and_activity_rows" }; } function summarizeBusinessOverviewRows(input: { incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null; lifecycleResult: AddressMcpQueryExecutorResult | null; taxResult: AddressMcpQueryExecutorResult | null; tradingMarginResult: AddressMcpQueryExecutorResult | null; receivablesResult: AddressMcpQueryExecutorResult | null; payablesResult: AddressMcpQueryExecutorResult | null; openContractsResult: AddressMcpQueryExecutorResult | null; inventoryOnHandResult: AddressMcpQueryExecutorResult | null; inventoryAgingResult: AddressMcpQueryExecutorResult | null; }): string | null { const parts: string[] = []; if (input.incomingResult && !input.incomingResult.error) { parts.push(`${input.incomingResult.fetched_rows} incoming rows fetched, ${input.incomingResult.matched_rows} matched`); } if (input.outgoingResult && !input.outgoingResult.error) { parts.push(`${input.outgoingResult.fetched_rows} outgoing rows fetched, ${input.outgoingResult.matched_rows} matched`); } if (input.lifecycleResult && !input.lifecycleResult.error) { parts.push(`${input.lifecycleResult.fetched_rows} activity/document rows fetched, ${input.lifecycleResult.matched_rows} matched`); } if (input.taxResult && !input.taxResult.error) { parts.push(`${input.taxResult.fetched_rows} VAT/tax rows fetched, ${input.taxResult.matched_rows} matched`); } if (input.tradingMarginResult && !input.tradingMarginResult.error) { parts.push(`${input.tradingMarginResult.fetched_rows} trading-margin document rows fetched, ${input.tradingMarginResult.matched_rows} matched`); } if (input.receivablesResult && !input.receivablesResult.error) { parts.push(`${input.receivablesResult.fetched_rows} receivables rows fetched, ${input.receivablesResult.matched_rows} matched`); } if (input.payablesResult && !input.payablesResult.error) { parts.push(`${input.payablesResult.fetched_rows} payables rows fetched, ${input.payablesResult.matched_rows} matched`); } if (input.openContractsResult && !input.openContractsResult.error) { parts.push(`${input.openContractsResult.fetched_rows} open-contract rows fetched, ${input.openContractsResult.matched_rows} matched`); } if (input.inventoryOnHandResult && !input.inventoryOnHandResult.error) { parts.push(`${input.inventoryOnHandResult.fetched_rows} inventory on-hand rows fetched, ${input.inventoryOnHandResult.matched_rows} matched`); } if (input.inventoryAgingResult && !input.inventoryAgingResult.error) { parts.push(`${input.inventoryAgingResult.fetched_rows} inventory aging rows fetched, ${input.inventoryAgingResult.matched_rows} matched`); } return parts.length > 0 ? parts.join("; ") : null; } function buildBusinessOverviewConfirmedFacts(derived: AssistantMcpDiscoveryDerivedBusinessOverview | null): string[] { if (!derived) { return []; } const facts: string[] = []; const organization = derived.organization_scope ? ` по организации ${derived.organization_scope}` : ""; const period = derived.period_scope ? ` за ${derived.period_scope}` : " за все доступное проверенное окно"; if (derived.incoming_customer_revenue.rows_with_amount > 0) { facts.push( `В 1С подтверждены входящие поступления${organization}${period}: ${derived.incoming_customer_revenue.total_amount_human_ru} по ${derived.incoming_customer_revenue.rows_with_amount} строкам с суммой.` ); } if (derived.outgoing_supplier_payout.rows_with_amount > 0) { facts.push( `В 1С подтверждены исходящие платежи/списания${organization}${period}: ${derived.outgoing_supplier_payout.total_amount_human_ru} по ${derived.outgoing_supplier_payout.rows_with_amount} строкам с суммой.` ); } if (derived.top_customers.length > 0) { const leader = derived.top_customers[0]; facts.push( `Самый крупный подтвержденный клиент в проверенном срезе: ${leader.axis_value} — ${leader.total_amount_human_ru}.` ); } if (derived.top_suppliers.length > 0) { const leader = derived.top_suppliers[0]; facts.push( `Самый крупный подтвержденный поставщик/получатель исходящих платежей в проверенном срезе: ${leader.axis_value} — ${leader.total_amount_human_ru}.` ); } if (derived.yearly_breakdown.length > 0) { facts.push( `Годовая раскладка операционного денежного потока построена по подтвержденным строкам 1С за ${yearCountHumanRu(derived.yearly_breakdown.length)}.` ); } if (derived.activity_period) { facts.push( `Подтвержденное окно активности в 1С: ${derived.activity_period.first_activity_date} — ${derived.activity_period.latest_activity_date}.` ); } if (derived.tax_position) { const taxDirection = derived.tax_position.net_vat_direction === "vat_to_pay" ? "к уплате" : derived.tax_position.net_vat_direction === "vat_to_recover_or_offset" ? "к вычету/зачету" : "сбалансирован"; facts.push( `НДС-позиция за ${derived.tax_position.period_scope} подтверждена по книгам продаж/покупок: продажи ${derived.tax_position.sales_vat_amount_human_ru}, покупки/вычеты ${derived.tax_position.purchase_vat_amount_human_ru}, нетто ${taxDirection} ${derived.tax_position.net_vat_amount_human_ru}.` ); } if (derived.trading_margin_proxy) { const proxy = derived.trading_margin_proxy; const marginText = proxy.margin_to_revenue_pct === null ? "не рассчитана" : `${proxy.margin_to_revenue_pct}%`; facts.push( `Торговый margin proxy за ${proxy.period_scope} подтвержден по товарным документам продаж/поступлений: выручка продаж ${proxy.sales_revenue_human_ru}, закупочный документный след ${proxy.purchase_cost_proxy_human_ru}, валовый спред proxy ${proxy.gross_spread_proxy_human_ru}, маржинальность к выручке ${marginText}. Это не чистая прибыль и не бухгалтерский финрезультат.` ); } if (derived.debt_position) { const debtDirection = derived.debt_position.net_debt_position_direction === "net_receivable" ? "в пользу дебиторки" : derived.debt_position.net_debt_position_direction === "net_payable" ? "в сторону кредиторки" : "сбалансировано"; facts.push( `Долговая позиция на ${derived.debt_position.as_of_date} подтверждена по срезам дебиторки/кредиторки 1С: дебиторка ${derived.debt_position.receivables.total_amount_human_ru}, кредиторка ${derived.debt_position.payables.total_amount_human_ru}, нетто ${debtDirection} ${derived.debt_position.net_debt_position_amount_human_ru}.` ); } if (derived.debt_open_settlement_quality) { const quality = derived.debt_open_settlement_quality; const leader = quality.top_contracts[0]; const leaderShareText = leader?.share_of_gross_open_amount_pct === null || leader?.share_of_gross_open_amount_pct === undefined ? "" : ` (${leader.share_of_gross_open_amount_pct}%)`; const leaderText = leader ? ` Крупнейший открытый договор: ${leader.contract}${leader.counterparty ? ` / ${leader.counterparty}` : ""} — ${leader.total_amount_human_ru}${leaderShareText}.` : ""; facts.push( `Качество открытых расчетов на ${quality.as_of_date} проверено по договорным остаткам 60/62/76: брутто ${quality.gross_open_amount_human_ru}, договоров ${quality.unique_contracts}, контрагентов ${quality.unique_counterparties}.${leaderText}` ); if (quality.age_signal?.oldest_start_date) { const ageText = quality.age_signal.max_age_days === null ? "" : `, максимальный возраст сигнала ${quality.age_signal.max_age_days} дн.`; facts.push( `Возрастной сигнал открытых расчетов подтвержден по датам договоров: самая ранняя дата договора ${quality.age_signal.oldest_start_date}${ageText}. Это не договорная просрочка и не due-date анализ.` ); } } if (derived.debt_staleness_risk_proxy) { const proxy = derived.debt_staleness_risk_proxy; const counterpartyText = proxy.top_contract_counterparty ? ` / ${proxy.top_contract_counterparty}` : ""; facts.push( `Staleness risk proxy открытых расчетов на ${proxy.as_of_date}: самый старый договорный сигнал ${proxy.oldest_contract_start_date}, возраст ${proxy.max_contract_age_days} дн.; старейший крупный договор ${proxy.top_contract}${counterpartyText} держит ${proxy.top_contract_amount_human_ru} (${proxy.top_contract_share_pct}% брутто открытых остатков); оценка ${debtStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная просрочка, не кредитный риск и не due-date aging.` ); } if (derived.inventory_position) { const leader = derived.inventory_position.top_items[0]; const leaderText = leader ? ` Крупнейшая подтвержденная позиция: ${leader.item} — ${leader.total_amount_human_ru}.` : ""; facts.push( `Складской срез на ${derived.inventory_position.as_of_date} подтвержден по 1С: остаток ${derived.inventory_position.total_amount_human_ru} по ${derived.inventory_position.rows_with_amount} строкам с суммой и ${derived.inventory_position.rows_with_quantity} строкам с количеством.${leaderText}` ); if (derived.inventory_position.aging_signal?.oldest_purchase_date) { const ageText = derived.inventory_position.aging_signal.max_age_days === null ? "" : `, максимальный возраст сигнала ${derived.inventory_position.aging_signal.max_age_days} дн.`; facts.push( `Возрастной сигнал склада подтвержден по найденным строкам закупок: самая ранняя дата ${derived.inventory_position.aging_signal.oldest_purchase_date}${ageText}.` ); } } if (derived.inventory_turnover_proxy) { const proxy = derived.inventory_turnover_proxy; const ratioText = proxy.sales_to_stock_amount_ratio === null ? "не рассчитано" : `${proxy.sales_to_stock_amount_ratio}x`; const stockShareText = proxy.stock_to_sales_revenue_pct === null ? "не рассчитана" : `${proxy.stock_to_sales_revenue_pct}%`; facts.push( `Оборотный proxy склада за ${proxy.period_scope} подтвержден по продажным документам и складскому остатку: продажи ${proxy.sales_revenue_human_ru}, остаток на ${proxy.as_of_date} ${proxy.inventory_amount_human_ru}, sales-to-stock ratio ${ratioText}, остаток к продажам ${stockShareText}. Это не полноценная складская ликвидность, не FIFO-оборачиваемость и не анализ устаревания.` ); } if (derived.inventory_staleness_risk_proxy) { const proxy = derived.inventory_staleness_risk_proxy; facts.push( `Staleness risk proxy склада на ${proxy.as_of_date}: самая ранняя дата закупочного сигнала ${proxy.oldest_purchase_date}, возраст ${proxy.max_purchase_age_days} дн., sales-to-stock ${proxy.sales_to_stock_amount_ratio}x, оценка ${inventoryStalenessRiskBandRu(proxy.risk_band)}. Это не подтвержденная неликвидность, не резерв и не ликвидационная стоимость.` ); } return facts; } function buildBusinessOverviewInferredFacts(derived: AssistantMcpDiscoveryDerivedBusinessOverview | null): string[] { if (!derived) { return []; } if ( derived.incoming_customer_revenue.rows_with_amount <= 0 && derived.outgoing_supplier_payout.rows_with_amount <= 0 ) { return []; } const direction = derived.net_direction === "net_incoming" ? "денежный поток в проверенном срезе больше входящий, чем исходящий" : derived.net_direction === "net_outgoing" ? "денежный поток в проверенном срезе больше исходящий, чем входящий" : "входящий и исходящий денежный поток в проверенном срезе примерно сбалансированы"; const supplierLeader = derived.top_suppliers[0]; const supplierSharePct = supplierLeader ? percentageOfTotal(supplierLeader.total_amount, derived.outgoing_supplier_payout.total_amount) : null; const strongestIncomingYear = [...derived.yearly_breakdown] .filter((bucket) => bucket.incoming_total_amount > 0) .sort((left, right) => right.incoming_total_amount - left.incoming_total_amount || left.year_bucket.localeCompare(right.year_bucket))[0]; const strongestNetYear = [...derived.yearly_breakdown] .filter((bucket) => bucket.net_amount !== 0) .sort((left, right) => right.net_amount - left.net_amount || left.year_bucket.localeCompare(right.year_bucket))[0]; return [ `Расчетное нетто по найденным строкам: ${derived.net_amount_human_ru}; ${direction}.`, supplierLeader ? supplierSharePct !== null ? `Крупнейший подтвержденный поставщик/получатель исходящих платежей ${supplierLeader.axis_value} держит около ${supplierSharePct}% проверенного исходящего потока (${supplierLeader.total_amount_human_ru}). Это procurement concentration proxy по найденным строкам, а не полный vendor-risk аудит.` : `Крупнейший подтвержденный поставщик/получатель исходящих платежей в проверенном срезе: ${supplierLeader.axis_value} — ${supplierLeader.total_amount_human_ru}.` : null, strongestIncomingYear ? `Самый сильный год по подтвержденным входящим поступлениям: ${strongestIncomingYear.year_bucket} (${strongestIncomingYear.incoming_total_amount_human_ru}).` : null, strongestNetYear ? `Лучший год по расчетному операционному нетто найденных строк: ${strongestNetYear.year_bucket} (${netDirectionFromAmount(strongestNetYear.net_amount) === "net_outgoing" ? "нетто исходящее" : "нетто в плюс"} ${strongestNetYear.net_amount_human_ru}). Это не бухгалтерская прибыль.` : null, "Это операционный денежный сигнал по найденным строкам 1С, а не прибыль, маржа или бухгалтерское заключение о здоровье бизнеса." ].filter((fact): fact is string => Boolean(fact)); } function buildBusinessOverviewUnknownFacts(derived: AssistantMcpDiscoveryDerivedBusinessOverview | null): string[] { const missing = new Set(derived?.missing_signal_families ?? [ "profit_margin", "debt_position", "tax_position", "inventory_position" ]); const unknowns = [ missing.has("profit_margin") ? "Прибыль и маржа этим бизнес-обзором не подтверждены: нужны себестоимость, расходы и закрывающие документы." : null, missing.has("accounting_profit_margin") ? "Чистая прибыль, бухгалтерская маржа и финрезультат этим бизнес-обзором не подтверждены: торговый proxy показывает только документную разницу продаж и поступлений без расходов, закрытия периода и точной себестоимости продаж." : null, missing.has("debt_quality") ? "Качество дебиторки/кредиторки этим бизнес-обзором не подтверждено: нужен отдельный долговой срез." : null, missing.has("debt_position") ? "Дебиторка/кредиторка этим бизнес-обзором не подтверждены: нужен отдельный долговой срез на явную дату." : null, missing.has("debt_aging_quality") ? "Качество долга и просрочка этим бизнес-обзором не подтверждены: текущий долговой срез показывает только суммы на дату, без aging/due-date анализа." : null, missing.has("debt_open_settlement_quality") ? "Качество открытых расчетов этим бизнес-обзором не подтверждено: нужен срез открытых договоров на явную дату." : null, missing.has("debt_due_date_aging_quality") ? "Просрочка и due-date aging этим бизнес-обзором не подтверждены: открытые договоры показывают концентрацию остатков, но не договорные сроки оплаты." : null, missing.has("tax_position") ? "Налоговая/VAT-позиция этим бизнес-обзором не подтверждена: нужен отдельный налоговый контур или явный проверяемый период." : null, missing.has("inventory_health") ? "Складская ликвидность и товарные остатки этим бизнес-обзором не подтверждены: нужен отдельный inventory-срез." : null, missing.has("inventory_position") ? "Складской остаток этим бизнес-обзором не подтвержден: нужен отдельный inventory-срез на явную дату." : null, missing.has("inventory_aging_quality") ? "Возраст, залежалость и ликвидность склада этим бизнес-обзором не подтверждены: текущий складской срез показывает только остаток на дату без полноценной оборачиваемости." : null, missing.has("inventory_turnover_quality") ? "Скорость продаж, оборачиваемость и ликвидность склада этим бизнес-обзором не подтверждены: нужен отдельный inventory/продажный анализ, а не только остаток на дату." : null, missing.has("inventory_liquidity_quality") ? "Полная складская ликвидность этим бизнес-обзором не подтверждена: sales-to-stock proxy показывает только соотношение продажных документов и остатка на дату, без FIFO-оборачиваемости, устаревания, резервов и ликвидационной стоимости." : null, missing.has("inventory_reserve_liquidation_quality") ? "Резервы, списания, подтвержденная неликвидность и ликвидационная стоимость склада этим бизнес-обзором не подтверждены: staleness proxy показывает только возраст закупочного сигнала и sales-to-stock, без управленческого решения о запасах." : null ].filter((item): item is string => Boolean(item)); if (derived?.coverage_limited_by_probe_limit) { unknowns.unshift("Полное покрытие бизнес-обзора не подтверждено: хотя бы один денежный probe достиг лимита строк."); } return unknowns; } function buildLifecycleConfirmedFacts(result: AddressMcpQueryExecutorResult, counterparty: string | null): string[] { if (result.error || result.matched_rows <= 0) { return []; } return [ counterparty ? `1C activity rows were found for counterparty ${counterparty}; matched_rows=${result.matched_rows}` : `1C activity rows were found for the requested counterparty scope; matched_rows=${result.matched_rows}` ]; } function checkedCounterpartySuffixRu(counterparty: string | null): string { return counterparty ? ` по контрагенту ${counterparty}` : ""; } function checkedPeriodSuffixRu(periodScope: string | null): string { return periodScope ? ` за ${periodScope}` : " в проверенном окне"; } function uncheckedPeriodBoundaryRu(periodScope: string | null): string { return periodScope ? ` вне периода ${periodScope}` : " без явно проверенного периода"; } function buildDocumentConfirmedFacts( result: AddressMcpQueryExecutorResult, counterparty: string | null, periodScope: string | null ): string[] { if (result.error || result.matched_rows <= 0) { return []; } return [`В 1С найдены строки документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`]; } function buildMovementConfirmedFacts( result: AddressMcpQueryExecutorResult, counterparty: string | null, periodScope: string | null ): string[] { if (result.error || result.matched_rows <= 0) { return []; } return [`В 1С найдены строки движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`]; } function buildValueFlowConfirmedFacts( result: AssistantMcpDiscoveryCoverageAwareQueryResult, counterparty: string | null, direction: AssistantMcpDiscoveryDerivedValueFlow["value_flow_direction"] ): string[] { if (result.error || result.matched_rows <= 0) { return []; } if (direction === "outgoing_supplier_payout") { return [ counterparty ? `1C supplier-payout rows were found for counterparty ${counterparty}` : "1C supplier-payout rows were found for the requested counterparty scope" ]; } return [ counterparty ? `1C value-flow rows were found for counterparty ${counterparty}` : "1C value-flow rows were found for the requested counterparty scope" ]; } function buildRankedValueFlowConfirmedFacts(derived: AssistantMcpDiscoveryDerivedRankedValueFlow | null): string[] { if (!derived || derived.ranked_values.length <= 0) { return []; } const leader = derived.ranked_values[0]; const directionLabel = derived.value_flow_direction === "outgoing_supplier_payout" ? "supplier-payout" : "incoming value-flow"; return [ `1C ${directionLabel} rows were ranked by counterparty for the checked scope; leader=${leader.axis_value}, rows_with_amount=${leader.rows_with_amount}` ]; } function buildBidirectionalValueFlowConfirmedFacts( derived: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null ): string[] { if (!derived) { return []; } const hasIncoming = derived.incoming_customer_revenue.rows_matched > 0; const hasOutgoing = derived.outgoing_supplier_payout.rows_matched > 0; if (derived.counterparty) { return [ `1C bidirectional value-flow rows were checked for counterparty ${derived.counterparty}: incoming=${hasIncoming ? "found" : "not_found"}, outgoing=${hasOutgoing ? "found" : "not_found"}` ]; } return [ `1C bidirectional value-flow rows were checked for the requested counterparty scope: incoming=${hasIncoming ? "found" : "not_found"}, outgoing=${hasOutgoing ? "found" : "not_found"}` ]; } function inventoryLabelRu(intent: AddressIntent): string { if (intent === "inventory_supplier_stock_overlap_as_of_date") { return "связи поставщиков с товарным остатком"; } if (intent === "inventory_purchase_provenance_for_item") { return "закупочной истории позиции"; } if (intent === "inventory_sale_trace_for_item") { return "продаж по позиции"; } return "складского среза"; } function inventoryScopeSuffixRu(input: { intent: AddressIntent; item: string | null; counterparty: string | null; organization: string | null; asOfDate: string | null; dateScope: string | null; }): string { const parts: string[] = []; if (input.organization) { parts.push(`по организации ${input.organization}`); } if (input.item) { parts.push(`по позиции ${input.item}`); } if (input.intent === "inventory_supplier_stock_overlap_as_of_date" && input.counterparty) { parts.push(`по поставщику/контрагенту ${input.counterparty}`); } if (input.asOfDate) { parts.push(`на ${input.asOfDate}`); } else if (input.dateScope) { parts.push(`за ${input.dateScope}`); } return parts.length > 0 ? ` ${parts.join(", ")}` : ""; } function inventoryRowSample(row: Record): string | null { const item = rowInventoryItemValue(row); const quantity = rowQuantityValue(row); const warehouse = rowWarehouseValue(row); const counterparty = rowCounterpartyValue(row); const document = rowDocumentValue(row); const parts: string[] = []; if (item) { parts.push(item); } if (quantity !== null) { parts.push(`${quantity} шт.`); } if (warehouse) { parts.push(`склад ${warehouse}`); } if (counterparty) { parts.push(`контрагент ${counterparty}`); } if (document) { parts.push(`документ ${document}`); } return parts.length > 0 ? parts.join(", ") : null; } function buildInventoryConfirmedFacts( result: AddressMcpQueryExecutorResult, planner: AssistantMcpDiscoveryPlannerContract, intent: AddressIntent ): string[] { if (result.error || result.matched_rows <= 0) { return []; } const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope); const item = intent === "inventory_purchase_provenance_for_item" || intent === "inventory_sale_trace_for_item" ? firstEntityCandidate(planner) : null; const counterparty = intent === "inventory_supplier_stock_overlap_as_of_date" ? firstEntityCandidate(planner) : null; const scope = inventoryScopeSuffixRu({ intent, item, counterparty, organization: organizationScopeForPlanner(planner), asOfDate: asOfDateFromDateScope(dateScope), dateScope }); const samples = result.rows .slice(0, 3) .map((row) => inventoryRowSample(row)) .filter((value): value is string => Boolean(value)); const sampleSuffix = samples.length > 0 ? ` Примеры строк: ${samples.join("; ")}.` : ""; return [`В 1С найдены подтвержденные строки ${inventoryLabelRu(intent)}${scope}: ${result.matched_rows}.${sampleSuffix}`]; } function buildInventoryInferredFacts(result: AddressMcpQueryExecutorResult, intent: AddressIntent): string[] { if (result.error || result.fetched_rows <= 0) { return []; } if (result.matched_rows <= 0) { return [ `По ${inventoryLabelRu(intent)} удалось проверить только ограниченный срез 1С; подтвержденных строк этим поиском не найдено.` ]; } return [ `Вывод по ${inventoryLabelRu(intent)} ограничен найденными строками 1С и указанными датой, организацией, позицией или поставщиком.` ]; } function buildInventoryUnknownFacts( result: AddressMcpQueryExecutorResult | null, intent: AddressIntent, dateScope: string | null ): string[] { const facts = [ dateScope ? `Полный товарный контур вне проверенного среза ${dateScope} не подтвержден.` : "Полный товарный контур без явного проверенного периода или даты не подтвержден." ]; if (!result || result.error || result.matched_rows <= 0) { facts.unshift(`Подтвержденный факт по ${inventoryLabelRu(intent)} в проверенных строках 1С не найден.`); } return facts; } function buildLifecycleInferredFacts(result: AddressMcpQueryExecutorResult): string[] { if (result.error || result.fetched_rows <= 0) { return []; } const period = deriveActivityPeriod(result); if (!period) { return ["Business activity duration may be inferred only when confirmed 1C activity row dates are available"]; } return [ "Business activity duration may be inferred from first and latest confirmed 1C activity rows", `Activity window is bounded by first=${period.first_activity_date}, latest=${period.latest_activity_date}, matched_rows=${period.matched_rows}`, "Activity-window inference is not legal registration age" ]; } function buildDocumentInferredFacts( result: AddressMcpQueryExecutorResult, counterparty: string | null, periodScope: string | null ): string[] { if (result.error || result.fetched_rows <= 0) { return []; } if (result.matched_rows <= 0) { return [ `По документам${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк документов этим поиском не найдено.` ]; } return [ `Срез документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками документов, найденными этим поиском.` ]; } function buildMovementInferredFacts( result: AddressMcpQueryExecutorResult, counterparty: string | null, periodScope: string | null ): string[] { if (result.error || result.fetched_rows <= 0) { return []; } if (result.matched_rows <= 0) { return [ `По движениям${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк движений этим поиском не найдено.` ]; } return [ `Срез движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками движений, найденными этим поиском.` ]; } function buildValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerivedValueFlow | null): string[] { if (!derived) { return []; } const facts: string[] = []; if (derived.value_flow_direction === "outgoing_supplier_payout") { facts.push("Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows"); } else { facts.push("Counterparty value-flow total was calculated from confirmed 1C movement rows"); } if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") { facts.push( "Requested period coverage was recovered through monthly 1C value-flow probes" ); } if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) { facts.push("Counterparty monthly value-flow breakdown was grouped by month over confirmed 1C movement rows"); } return facts; } function buildRankedValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerivedRankedValueFlow | null): string[] { if (!derived) { return []; } const facts = ["Counterparty ranking was calculated from confirmed 1C movement rows grouped by counterparty"]; if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") { facts.push( "Requested period coverage for counterparty ranking was recovered through monthly 1C probes" ); } return facts; } function buildBidirectionalValueFlowInferredFacts( derived: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null ): string[] { if (!derived) { return []; } const facts = ["Counterparty net value-flow was calculated as incoming confirmed 1C rows minus outgoing confirmed 1C rows"]; if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") { facts.push( "Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes" ); } if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) { facts.push("Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows"); } return facts; } function buildLifecycleUnknownFacts(): string[] { return [ "Legal registration date is not proven by this MCP discovery pilot", "Business activity before the first confirmed 1C activity row is not proven by this MCP discovery pilot" ]; } function buildDocumentUnknownFacts(periodScope: string | null, counterparty: string | null): string[] { return [ `Полный исторический срез документов${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.` ]; } function buildMovementUnknownFacts(periodScope: string | null, counterparty: string | null): string[] { return [ `Полный исторический срез движений${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.` ]; } function buildValueFlowUnknownFacts( periodScope: string | null, direction: AssistantMcpDiscoveryDerivedValueFlow["value_flow_direction"], derived: AssistantMcpDiscoveryDerivedValueFlow | null ): string[] { const unknownFacts: string[] = []; if (derived?.coverage_limited_by_probe_limit) { unknownFacts.push("Complete requested-period coverage is not proven by the available checked rows"); } if (direction === "outgoing_supplier_payout") { unknownFacts.push( periodScope ? "Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot" : "Full all-time supplier-payout amount is not proven without an explicit checked period" ); return unknownFacts; } unknownFacts.push( 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" ); return unknownFacts; } function buildRankedValueFlowUnknownFacts( periodScope: string | null, derived: AssistantMcpDiscoveryDerivedRankedValueFlow | null ): string[] { const unknownFacts: string[] = []; if (derived?.coverage_limited_by_probe_limit) { unknownFacts.push("Complete requested-period ranking coverage is not proven by the available checked rows"); } unknownFacts.push( periodScope ? "Full ranking outside the checked period is not proven by this MCP discovery pilot" : "Full all-time counterparty ranking is not proven without an explicit checked period" ); return unknownFacts; } function buildBidirectionalValueFlowUnknownFacts( periodScope: string | null, derived: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null ): string[] { const unknownFacts: string[] = []; if (derived?.coverage_limited_by_probe_limit) { unknownFacts.push( "Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows" ); } unknownFacts.push( periodScope ? "Full bidirectional value-flow outside the checked period is not proven by this MCP discovery pilot" : "Full all-time bidirectional value-flow is not proven without an explicit checked period" ); return unknownFacts; } function buildEmptyEvidence( planner: AssistantMcpDiscoveryPlannerContract, dryRun: AssistantMcpDiscoveryRuntimeDryRunContract, probeResults: AssistantMcpDiscoveryProbeResult[], reason: string ): AssistantMcpDiscoveryEvidenceContract { return resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, unknownFacts: [reason], queryLimitations: [reason], recommendedNextProbe: dryRun.user_facing_fallback }); } function pilotScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): AssistantMcpDiscoveryPilotScope { switch (planner.selected_chain_id) { case "catalog_drilldown": case "metadata_lane_clarification": case "metadata_inspection": return "metadata_inspection_v1"; case "inventory_stock_snapshot": case "inventory_supplier_overlap": case "inventory_purchase_provenance": case "inventory_sale_trace": return "inventory_route_template_v1"; case "movement_evidence": return "counterparty_movement_evidence_query_movements_v1"; case "value_flow_comparison": case "value_flow_ranking": case "value_flow": return valueFlowPilotProfile(planner).scope; case "business_overview": return "business_overview_route_template_v1"; case "document_evidence": return "counterparty_document_evidence_query_documents_v1"; case "lifecycle": return "counterparty_lifecycle_query_documents_v1"; case "entity_resolution": return "entity_resolution_search_v1"; } } export async function executeAssistantMcpDiscoveryPilot( planner: AssistantMcpDiscoveryPlannerContract, deps: AssistantMcpDiscoveryPilotExecutorDeps = DEFAULT_DEPS ): Promise { const runtimeDeps: ResolvedAssistantMcpDiscoveryPilotExecutorDeps = { ...DEFAULT_DEPS, ...deps }; const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner); const reasonCodes = [...dryRun.reason_codes]; const executedPrimitives: string[] = []; const skippedPrimitives: string[] = []; const probeResults: AssistantMcpDiscoveryProbeResult[] = []; const queryLimitations: string[] = []; const pilotScope = pilotScopeForPlanner(planner); 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: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "blocked", pilot_scope: pilotScope, dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_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: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "skipped_needs_clarification", pilot_scope: pilotScope, dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["MCP discovery pilot needs more scope before execution"], reason_codes: reasonCodes }; } const metadataPilotEligible = isMetadataPilotEligible(planner); const documentPilotEligible = isDocumentEvidencePilotEligible(planner); const movementPilotEligible = isMovementEvidencePilotEligible(planner); const lifecyclePilotEligible = isLifecyclePilotEligible(planner); const valueFlowPilotEligible = isValueFlowPilotEligible(planner); const businessOverviewPilotEligible = isBusinessOverviewPilotEligible(planner); const entityResolutionPilotEligible = isEntityResolutionPilotEligible(planner); const inventoryPilotEligible = isInventoryPilotEligible(planner); if ( !metadataPilotEligible && !documentPilotEligible && !movementPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible && !businessOverviewPilotEligible && !entityResolutionPilotEligible && !inventoryPilotEligible ) { 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: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: pilotScope, dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_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); const organizationScope = organizationScopeForPlanner(planner); const aggregationAxis = aggregationAxisForPlanner(planner); const rankingNeed = rankingNeedForPlanner(planner); if (inventoryPilotEligible) { let queryResult: AddressMcpQueryExecutorResult | null = null; const inventoryIntent = inventoryIntentForPlanner(planner); const executablePrimitive = inventoryExecutablePrimitiveForPlanner(planner); if (!inventoryIntent || !executablePrimitive) { pushReason(reasonCodes, "pilot_inventory_exact_recipe_not_mapped"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Inventory exact recipe is not mapped"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "inventory_route_template_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Inventory exact recipe is not mapped"], reason_codes: reasonCodes }; } const filters = buildInventoryExactFilters(planner); const selection = selectAddressRecipe(inventoryIntent, filters); if (selection.missing_required_filters.length > 0) { pushReason(reasonCodes, "pilot_inventory_exact_recipe_needs_required_filters"); const evidence = buildEmptyEvidence( planner, dryRun, probeResults, `Inventory exact recipe needs required filters: ${selection.missing_required_filters.join(", ")}` ); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "skipped_needs_clarification", pilot_scope: "inventory_route_template_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: [`Inventory exact recipe needs required filters: ${selection.missing_required_filters.join(", ")}`], reason_codes: reasonCodes }; } if (!selection.selected_recipe) { pushReason(reasonCodes, "pilot_inventory_exact_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Inventory exact recipe is not available"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "inventory_route_template_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Inventory exact recipe is not available"], reason_codes: reasonCodes }; } pushReason(reasonCodes, "pilot_inventory_exact_recipe_selected"); const recipePlan = buildAddressRecipePlan(selection.selected_recipe, filters); for (const step of dryRun.execution_steps) { if (step.primitive_id !== executablePrimitive) { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, `pilot_inventory_exact_bridge_executes_${executablePrimitive}`)); continue; } queryResult = await runtimeDeps.executeAddressMcpQuery({ query: recipePlan.query, limit: recipePlan.limit, account_scope: recipePlan.account_scope }); pushUnique(executedPrimitives, step.primitive_id); probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult)); if (queryResult.error) { pushUnique(queryLimitations, queryResult.error); pushReason(reasonCodes, "pilot_inventory_exact_mcp_error"); } else { pushReason(reasonCodes, "pilot_inventory_exact_mcp_executed"); } } const sourceRowsSummary = queryResult ? summarizeInventoryRows(queryResult) : null; const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildInventoryConfirmedFacts(queryResult, planner, inventoryIntent) : [], inferredFacts: queryResult ? buildInventoryInferredFacts(queryResult, inventoryIntent) : [], unknownFacts: buildInventoryUnknownFacts( queryResult, inventoryIntent, toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope) ), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "inventory_route_template_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (businessOverviewPilotEligible) { let incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null; let outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null; let lifecycleResult: AddressMcpQueryExecutorResult | null = null; let taxResult: AddressMcpQueryExecutorResult | null = null; let tradingMarginResult: AddressMcpQueryExecutorResult | null = null; let receivablesResult: AddressMcpQueryExecutorResult | null = null; let payablesResult: AddressMcpQueryExecutorResult | null = null; let openContractsResult: AddressMcpQueryExecutorResult | null = null; let inventoryOnHandResult: AddressMcpQueryExecutorResult | null = null; let inventoryAgingResult: AddressMcpQueryExecutorResult | null = null; const valueFilters = buildValueFlowFilters(planner); const lifecycleFilters = buildLifecycleFilters(planner); const taxFilters = buildBusinessOverviewTaxFilters(planner); const tradingMarginFilters = buildBusinessOverviewTradingMarginFilters(planner); const debtFilters = buildBusinessOverviewDebtFilters(planner); const inventoryFilters = buildBusinessOverviewInventoryFilters(planner); const debtAsOfDate = toNonEmptyString(debtFilters?.as_of_date); const inventoryAsOfDate = toNonEmptyString(inventoryFilters?.as_of_date); const incomingSelection = selectAddressRecipe("customer_revenue_and_payments", valueFilters); const outgoingSelection = selectAddressRecipe("supplier_payouts_profile", valueFilters); const lifecycleSelection = selectAddressRecipe("counterparty_activity_lifecycle", lifecycleFilters); const taxSelection = taxFilters ? selectAddressRecipe("vat_liability_confirmed_for_tax_period", taxFilters) : null; const tradingMarginSelection = tradingMarginFilters ? selectAddressRecipe("inventory_trading_margin_proxy_for_organization", tradingMarginFilters) : null; const receivablesSelection = debtFilters ? selectAddressRecipe("receivables_confirmed_as_of_date", debtFilters) : null; const payablesSelection = debtFilters ? selectAddressRecipe("payables_confirmed_as_of_date", debtFilters) : null; const openContractsSelection = debtFilters ? selectAddressRecipe("open_contracts_confirmed_as_of_date", debtFilters) : null; const inventoryOnHandSelection = inventoryFilters ? selectAddressRecipe("inventory_on_hand_as_of_date", inventoryFilters) : null; const inventoryAgingSelection = inventoryFilters ? selectAddressRecipe("inventory_aging_by_purchase_date", inventoryFilters) : null; if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe || !lifecycleSelection.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_recipe_not_available"); const missing = [ incomingSelection.selected_recipe ? null : "customer_revenue_and_payments", outgoingSelection.selected_recipe ? null : "supplier_payouts_profile", lifecycleSelection.selected_recipe ? null : "counterparty_activity_lifecycle" ].filter((item): item is string => Boolean(item)); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Business overview recipe is not available"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "business_overview_route_template_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: [`Business overview recipe is not available: ${missing.join(", ")}`], reason_codes: reasonCodes }; } pushReason(reasonCodes, "pilot_business_overview_recipes_selected"); if (taxSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_tax_recipe_selected"); } else if (!taxFilters) { pushReason(reasonCodes, "pilot_business_overview_tax_probe_skipped_without_explicit_period"); } else { pushReason(reasonCodes, "pilot_business_overview_tax_recipe_not_available"); pushUnique(queryLimitations, "Business overview VAT/tax probe requires an executable tax-period recipe"); } if (tradingMarginSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_trading_margin_recipe_selected"); } else if (!tradingMarginFilters) { pushReason(reasonCodes, "pilot_business_overview_trading_margin_probe_skipped_without_explicit_period"); } else { pushReason(reasonCodes, "pilot_business_overview_trading_margin_recipe_not_available"); pushUnique(queryLimitations, "Business overview trading-margin proxy requires an executable explicit-period purchase/sale document recipe"); } if (receivablesSelection?.selected_recipe && payablesSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_debt_recipes_selected"); } else if (!debtFilters) { pushReason(reasonCodes, "pilot_business_overview_debt_probe_skipped_without_explicit_as_of_date"); } else { pushReason(reasonCodes, "pilot_business_overview_debt_recipe_not_available"); pushUnique(queryLimitations, "Business overview debt-position probe requires executable receivables/payables as-of-date recipes"); } if (openContractsSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_open_contracts_recipe_selected"); } else if (!debtFilters) { pushReason(reasonCodes, "pilot_business_overview_open_contracts_probe_skipped_without_explicit_as_of_date"); } else { pushReason(reasonCodes, "pilot_business_overview_open_contracts_recipe_not_available"); pushUnique(queryLimitations, "Business overview open-settlement quality probe requires executable open-contracts as-of-date recipe"); } if (inventoryOnHandSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_inventory_on_hand_recipe_selected"); if (inventoryAgingSelection?.selected_recipe) { pushReason(reasonCodes, "pilot_business_overview_inventory_aging_recipe_selected"); } } else if (!inventoryFilters) { pushReason(reasonCodes, "pilot_business_overview_inventory_probe_skipped_without_explicit_as_of_date"); } else { pushReason(reasonCodes, "pilot_business_overview_inventory_recipe_not_available"); pushUnique(queryLimitations, "Business overview inventory-position probe requires an executable inventory on-hand as-of-date recipe"); } for (const step of dryRun.execution_steps) { if (step.primitive_id === "query_movements") { const incomingExecution = await executeCoverageAwareValueFlowQuery({ primitiveId: step.primitive_id, recipePlanBuilder: (scopedFilters) => buildAddressRecipePlan(incomingSelection.selected_recipe!, scopedFilters), baseFilters: valueFilters, dateScope, maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count, maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe, deps: runtimeDeps }); const outgoingExecution = await executeCoverageAwareValueFlowQuery({ primitiveId: step.primitive_id, recipePlanBuilder: (scopedFilters) => buildAddressRecipePlan(outgoingSelection.selected_recipe!, scopedFilters), baseFilters: valueFilters, dateScope, maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count, maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe, deps: runtimeDeps }); incomingResult = incomingExecution.result; outgoingResult = outgoingExecution.result; if (taxSelection?.selected_recipe) { const taxPlan = buildAddressRecipePlan(taxSelection.selected_recipe, taxFilters!); taxResult = await runtimeDeps.executeAddressMcpQuery({ query: taxPlan.query, limit: taxPlan.limit, account_scope: taxPlan.account_scope }); } if (receivablesSelection?.selected_recipe && payablesSelection?.selected_recipe) { const receivablesPlan = buildAddressRecipePlan(receivablesSelection.selected_recipe, debtFilters!); receivablesResult = await runtimeDeps.executeAddressMcpQuery({ query: receivablesPlan.query, limit: receivablesPlan.limit, account_scope: receivablesPlan.account_scope }); const payablesPlan = buildAddressRecipePlan(payablesSelection.selected_recipe, debtFilters!); payablesResult = await runtimeDeps.executeAddressMcpQuery({ query: payablesPlan.query, limit: payablesPlan.limit, account_scope: payablesPlan.account_scope }); } if (openContractsSelection?.selected_recipe) { const openContractsPlan = buildAddressRecipePlan(openContractsSelection.selected_recipe, debtFilters!); openContractsResult = await runtimeDeps.executeAddressMcpQuery({ query: openContractsPlan.query, limit: openContractsPlan.limit, account_scope: openContractsPlan.account_scope }); } if (inventoryOnHandSelection?.selected_recipe) { const inventoryOnHandPlan = buildAddressRecipePlan(inventoryOnHandSelection.selected_recipe, inventoryFilters!); inventoryOnHandResult = await runtimeDeps.executeAddressMcpQuery({ query: inventoryOnHandPlan.query, limit: inventoryOnHandPlan.limit, account_scope: inventoryOnHandPlan.account_scope }); if (inventoryAgingSelection?.selected_recipe) { const inventoryAgingPlan = buildAddressRecipePlan(inventoryAgingSelection.selected_recipe, inventoryFilters!); inventoryAgingResult = await runtimeDeps.executeAddressMcpQuery({ query: inventoryAgingPlan.query, limit: inventoryAgingPlan.limit, account_scope: inventoryAgingPlan.account_scope }); } } pushUnique(executedPrimitives, step.primitive_id); probeResults.push(...incomingExecution.probe_results, ...outgoingExecution.probe_results); if (taxResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, taxResult)); } if (receivablesResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, receivablesResult)); } if (payablesResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, payablesResult)); } if (openContractsResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, openContractsResult)); } if (inventoryOnHandResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, inventoryOnHandResult)); } if (inventoryAgingResult) { probeResults.push(queryResultToProbeResult(step.primitive_id, inventoryAgingResult)); } for (const limitation of [...incomingExecution.query_limitations, ...outgoingExecution.query_limitations]) { pushUnique(queryLimitations, limitation); } if (incomingResult?.error) { pushReason(reasonCodes, "pilot_business_overview_incoming_query_mcp_error"); } if (outgoingResult?.error) { pushReason(reasonCodes, "pilot_business_overview_outgoing_query_mcp_error"); } if (!incomingResult?.error || !outgoingResult?.error) { pushReason(reasonCodes, "pilot_business_overview_query_movements_mcp_executed"); } if (taxResult?.error) { pushUnique(queryLimitations, taxResult.error); pushReason(reasonCodes, "pilot_business_overview_tax_query_mcp_error"); } else if (taxResult) { pushReason(reasonCodes, "pilot_business_overview_tax_query_mcp_executed"); } if (receivablesResult?.error) { pushUnique(queryLimitations, receivablesResult.error); pushReason(reasonCodes, "pilot_business_overview_receivables_query_mcp_error"); } else if (receivablesResult) { pushReason(reasonCodes, "pilot_business_overview_receivables_query_mcp_executed"); } if (payablesResult?.error) { pushUnique(queryLimitations, payablesResult.error); pushReason(reasonCodes, "pilot_business_overview_payables_query_mcp_error"); } else if (payablesResult) { pushReason(reasonCodes, "pilot_business_overview_payables_query_mcp_executed"); } if ( (receivablesResult && !receivablesResult.error) || (payablesResult && !payablesResult.error) ) { pushReason(reasonCodes, "pilot_business_overview_debt_query_mcp_executed"); } if (openContractsResult?.error) { pushUnique(queryLimitations, openContractsResult.error); pushReason(reasonCodes, "pilot_business_overview_open_contracts_query_mcp_error"); } else if (openContractsResult) { pushReason(reasonCodes, "pilot_business_overview_open_contracts_query_mcp_executed"); } if (inventoryOnHandResult?.error) { pushUnique(queryLimitations, inventoryOnHandResult.error); pushReason(reasonCodes, "pilot_business_overview_inventory_on_hand_query_mcp_error"); } else if (inventoryOnHandResult) { pushReason(reasonCodes, "pilot_business_overview_inventory_on_hand_query_mcp_executed"); } if (inventoryAgingResult?.error) { pushUnique(queryLimitations, inventoryAgingResult.error); pushReason(reasonCodes, "pilot_business_overview_inventory_aging_query_mcp_error"); } else if (inventoryAgingResult) { pushReason(reasonCodes, "pilot_business_overview_inventory_aging_query_mcp_executed"); } if ( (inventoryOnHandResult && !inventoryOnHandResult.error) || (inventoryAgingResult && !inventoryAgingResult.error) ) { pushReason(reasonCodes, "pilot_business_overview_inventory_query_mcp_executed"); } continue; } if (step.primitive_id === "query_documents") { const lifecyclePlan = buildAddressRecipePlan(lifecycleSelection.selected_recipe, lifecycleFilters); lifecycleResult = await runtimeDeps.executeAddressMcpQuery({ query: lifecyclePlan.query, limit: lifecyclePlan.limit, account_scope: lifecyclePlan.account_scope }); pushUnique(executedPrimitives, step.primitive_id); probeResults.push(queryResultToProbeResult(step.primitive_id, lifecycleResult)); if (tradingMarginSelection?.selected_recipe) { const tradingMarginPlan = buildAddressRecipePlan(tradingMarginSelection.selected_recipe, tradingMarginFilters!); tradingMarginResult = await runtimeDeps.executeAddressMcpQuery({ query: tradingMarginPlan.query, limit: tradingMarginPlan.limit, account_scope: tradingMarginPlan.account_scope }); probeResults.push(queryResultToProbeResult(step.primitive_id, tradingMarginResult)); } if (lifecycleResult.error) { pushUnique(queryLimitations, lifecycleResult.error); pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_error"); } else { pushReason(reasonCodes, "pilot_business_overview_query_documents_mcp_executed"); } if (tradingMarginResult?.error) { pushUnique(queryLimitations, tradingMarginResult.error); pushReason(reasonCodes, "pilot_business_overview_trading_margin_query_mcp_error"); } else if (tradingMarginResult) { pushReason(reasonCodes, "pilot_business_overview_trading_margin_query_mcp_executed"); } continue; } skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_business_overview_derives_aggregate_coverage_and_explanation")); } const derivedBusinessOverview = deriveBusinessOverview({ incomingResult, outgoingResult, lifecycleResult, taxResult, tradingMarginResult, receivablesResult, payablesResult, openContractsResult, debtAsOfDate, inventoryOnHandResult, inventoryAgingResult, inventoryAsOfDate, organizationScope, periodScope: dateScope }); if (derivedBusinessOverview) { pushReason(reasonCodes, "pilot_derived_business_overview_from_confirmed_rows"); if (derivedBusinessOverview.top_customers.length > 0) { pushReason(reasonCodes, "pilot_derived_business_overview_top_customers_from_confirmed_rows"); } if (derivedBusinessOverview.top_suppliers.length > 0) { pushReason(reasonCodes, "pilot_derived_business_overview_top_suppliers_from_confirmed_rows"); } if (derivedBusinessOverview.yearly_breakdown.length > 0) { pushReason(reasonCodes, "pilot_derived_business_overview_yearly_operating_breakdown_from_confirmed_rows"); } if (derivedBusinessOverview.activity_period) { pushReason(reasonCodes, "pilot_derived_business_overview_activity_window_from_confirmed_rows"); } if (derivedBusinessOverview.tax_position) { pushReason(reasonCodes, "pilot_derived_business_overview_tax_position_from_confirmed_rows"); } if (derivedBusinessOverview.trading_margin_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_trading_margin_proxy_from_confirmed_rows"); } if (derivedBusinessOverview.debt_position) { pushReason(reasonCodes, "pilot_derived_business_overview_debt_position_from_confirmed_rows"); } if (derivedBusinessOverview.debt_open_settlement_quality) { pushReason(reasonCodes, "pilot_derived_business_overview_open_settlement_quality_from_confirmed_rows"); if (derivedBusinessOverview.debt_open_settlement_quality.age_signal) { pushReason(reasonCodes, "pilot_derived_business_overview_debt_age_signal_from_contract_dates"); } } if (derivedBusinessOverview.debt_staleness_risk_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_debt_staleness_risk_proxy_from_confirmed_rows"); } if (derivedBusinessOverview.inventory_position) { pushReason(reasonCodes, "pilot_derived_business_overview_inventory_position_from_confirmed_rows"); } if (derivedBusinessOverview.inventory_turnover_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_inventory_turnover_proxy_from_confirmed_rows"); } if (derivedBusinessOverview.inventory_staleness_risk_proxy) { pushReason(reasonCodes, "pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows"); } } const sourceRowsSummary = summarizeBusinessOverviewRows({ incomingResult, outgoingResult, lifecycleResult, taxResult, tradingMarginResult, receivablesResult, payablesResult, openContractsResult, inventoryOnHandResult, inventoryAgingResult }); const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: buildBusinessOverviewConfirmedFacts(derivedBusinessOverview), inferredFacts: buildBusinessOverviewInferredFacts(derivedBusinessOverview), unknownFacts: buildBusinessOverviewUnknownFacts(derivedBusinessOverview), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "business_overview_route_template_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, derived_business_overview: derivedBusinessOverview, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (metadataPilotEligible) { let metadataResult: AddressMcpMetadataRowsResult | null = null; const metadataScope = metadataScopeForPlanner(planner); const requestedMetaTypes = metadataTypesForPlanner(planner); if (planner.selected_chain_id === "catalog_drilldown" && metadataScope) { pushReason(reasonCodes, "pilot_catalog_drilldown_metadata_scope_seeded_from_surface_ref"); } for (const step of dryRun.execution_steps) { if (step.primitive_id !== "inspect_1c_metadata") { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_metadata_uses_only_inspect_1c_metadata")); continue; } metadataResult = await runtimeDeps.executeAddressMcpMetadata({ meta_type: requestedMetaTypes, name_mask: metadataScope ?? undefined, limit: planner.discovery_plan.execution_budget.max_rows_per_probe }); pushUnique(executedPrimitives, step.primitive_id); probeResults.push(metadataResultToProbeResult(step.primitive_id, metadataResult)); if (metadataResult.error) { pushUnique(queryLimitations, metadataResult.error); pushReason(reasonCodes, "pilot_inspect_1c_metadata_mcp_error"); } else { pushReason(reasonCodes, "pilot_inspect_1c_metadata_mcp_executed"); } } const sourceRowsSummary = metadataResult ? summarizeMetadataRows(metadataResult) : null; const derivedMetadataSurface = deriveMetadataSurface( metadataResult, metadataScope, requestedMetaTypes, metadataScopeRankingAllowedForPlanner(planner) ); if (derivedMetadataSurface) { pushReason(reasonCodes, "pilot_derived_metadata_surface_from_confirmed_rows"); if (derivedMetadataSurface.route_family_selection_basis === "dominant_surface_objects") { pushReason(reasonCodes, "pilot_selected_metadata_route_family_from_dominant_surface_objects"); } if (derivedMetadataSurface.surface_object_ranking_applied) { pushReason(reasonCodes, "pilot_selected_metadata_route_family_from_surface_object_ranking"); } } const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: buildMetadataConfirmedFacts(derivedMetadataSurface), inferredFacts: buildMetadataInferredFacts(derivedMetadataSurface), unknownFacts: buildMetadataUnknownFacts(derivedMetadataSurface, metadataScope), sourceRowsSummary, queryLimitations, recommendedNextProbe: "inspect_1c_metadata" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "metadata_inspection_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_metadata_surface: derivedMetadataSurface, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (entityResolutionPilotEligible) { let queryResult: AddressMcpQueryExecutorResult | null = null; const requestedEntity = counterparty; if (isLowQualityEntityResolutionAnchor(requestedEntity)) { pushReason(reasonCodes, "pilot_entity_resolution_anchor_missing_or_low_quality"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Entity-resolution needs a clearer counterparty name"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "skipped_needs_clarification", pilot_scope: "entity_resolution_search_v1", dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Entity-resolution needs a clearer counterparty name"], reason_codes: reasonCodes }; } let derivedEntityResolution: AssistantMcpDiscoveryDerivedEntityResolution | null = null; for (const step of dryRun.execution_steps) { if (step.primitive_id === "search_business_entity") { queryResult = await runtimeDeps.executeAddressMcpQuery({ query: ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE.replaceAll( "__LIMIT__", String(ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT) ), limit: ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT }); pushUnique(executedPrimitives, step.primitive_id); probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult)); if (queryResult.error) { pushUnique(queryLimitations, queryResult.error); pushReason(reasonCodes, "pilot_search_business_entity_mcp_error"); } else { pushReason(reasonCodes, "pilot_search_business_entity_mcp_executed"); derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity); } continue; } if (step.primitive_id === "resolve_entity_reference") { if (!queryResult || queryResult.error) { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation())); continue; } if (!derivedEntityResolution) { derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity); } pushUnique(executedPrimitives, step.primitive_id); probeResults.push( buildEntityResolutionResolveProbeResult({ queryResult, resolution: derivedEntityResolution }) ); if (derivedEntityResolution?.resolution_status === "resolved") { pushReason(reasonCodes, "pilot_resolve_entity_reference_from_catalog_rows"); } else if (derivedEntityResolution?.resolution_status === "ambiguous") { pushReason(reasonCodes, "pilot_resolve_entity_reference_requires_clarification"); } else { pushReason(reasonCodes, "pilot_resolve_entity_reference_not_confirmed"); } continue; } if (step.primitive_id === "probe_coverage") { if (!queryResult || queryResult.error) { skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation())); continue; } if (!derivedEntityResolution) { derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity); } pushUnique(executedPrimitives, step.primitive_id); probeResults.push( buildEntityResolutionCoverageProbeResult({ resolution: derivedEntityResolution }) ); pushReason(reasonCodes, "pilot_probe_coverage_executed_for_entity_resolution"); if (derivedEntityResolution?.resolution_status === "resolved") { pushReason(reasonCodes, "pilot_entity_resolution_grounding_stable_for_downstream_probe"); } else if (derivedEntityResolution?.resolution_status === "ambiguous") { pushReason(reasonCodes, "pilot_entity_resolution_coverage_requires_clarification"); } else { pushReason(reasonCodes, "pilot_entity_resolution_coverage_not_confirmed"); } continue; } skippedPrimitives.push(step.primitive_id); probeResults.push(skippedProbeResult(step, "pilot_entity_resolution_step_not_implemented")); } const sourceRowsSummary = queryResult ? summarizeEntityResolutionRows(queryResult) : null; if (!derivedEntityResolution && queryResult && !queryResult.error) { derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity); } if (derivedEntityResolution?.resolution_status === "resolved") { pushReason(reasonCodes, "pilot_derived_entity_resolution_from_catalog_rows"); } if (derivedEntityResolution?.resolution_status === "ambiguous") { pushReason(reasonCodes, "pilot_entity_resolution_ambiguity_requires_clarification"); } if (derivedEntityResolution?.resolution_status === "not_found") { pushReason(reasonCodes, "pilot_entity_resolution_not_found_in_checked_catalog"); } const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: buildEntityResolutionConfirmedFacts(derivedEntityResolution), inferredFacts: buildEntityResolutionInferredFacts(derivedEntityResolution), unknownFacts: buildEntityResolutionUnknownFacts(derivedEntityResolution, requestedEntity), sourceRowsSummary, queryLimitations, recommendedNextProbe: null }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "entity_resolution_search_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_metadata_surface: null, derived_entity_resolution: derivedEntityResolution, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (documentPilotEligible) { let queryResult: AddressMcpQueryExecutorResult | null = null; const filters = buildLifecycleFilters(planner); const selection = selectAddressRecipe("list_documents_by_counterparty", filters); if (!selection.selected_recipe) { pushReason(reasonCodes, "pilot_document_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Document-evidence recipe is not available"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "counterparty_document_evidence_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Document-evidence recipe is not available"], reason_codes: reasonCodes }; } const recipePlan = 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 runtimeDeps.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 ? summarizeDocumentRows(queryResult) : null; const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty, dateScope) : [], inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult, counterparty, dateScope) : [], unknownFacts: buildDocumentUnknownFacts(dateScope, counterparty), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "counterparty_document_evidence_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (movementPilotEligible) { let queryResult: AddressMcpQueryExecutorResult | null = null; const filters = buildValueFlowFilters(planner); const selection = selectAddressRecipe("bank_operations_by_counterparty", filters); if (!selection.selected_recipe) { pushReason(reasonCodes, "pilot_movement_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Movement-evidence recipe is not available"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: "counterparty_movement_evidence_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Movement-evidence recipe is not available"], reason_codes: reasonCodes }; } const recipePlan = 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_only_executes_query_movements")); continue; } queryResult = await runtimeDeps.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 ? summarizeMovementRows(queryResult) : null; const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty, dateScope) : [], inferredFacts: queryResult ? buildMovementInferredFacts(queryResult, counterparty, dateScope) : [], unknownFacts: buildMovementUnknownFacts(dateScope, counterparty), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: "counterparty_movement_evidence_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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } if (valueFlowPilotEligible) { let queryResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null; const filters = buildValueFlowFilters(planner); const valueFlowProfile = valueFlowPilotProfile(planner); if (valueFlowProfile.direction === "bidirectional_net_value_flow") { let incomingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null; let outgoingResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null; const incomingSelection = selectAddressRecipe("customer_revenue_and_payments", filters); const outgoingSelection = selectAddressRecipe("supplier_payouts_profile", filters); if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe) { pushReason(reasonCodes, "pilot_bidirectional_value_flow_recipe_not_available"); const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Bidirectional value-flow recipes are not available"); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: valueFlowProfile.scope, dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Bidirectional value-flow recipes are not available"], reason_codes: reasonCodes }; } pushReason(reasonCodes, "pilot_bidirectional_value_flow_recipes_selected"); for (const step of dryRun.execution_steps) { if (step.primitive_id !== "query_movements") { skippedPrimitives.push(step.primitive_id); probeResults.push( skippedProbeResult(step, "pilot_bidirectional_value_flow_uses_two_query_movements_and_derives_net") ); continue; } const incomingExecution = await executeCoverageAwareValueFlowQuery({ primitiveId: step.primitive_id, recipePlanBuilder: (scopedFilters) => buildAddressRecipePlan(incomingSelection.selected_recipe!, scopedFilters), baseFilters: filters, dateScope, maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count, maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe, deps: runtimeDeps }); const outgoingExecution = await executeCoverageAwareValueFlowQuery({ primitiveId: step.primitive_id, recipePlanBuilder: (scopedFilters) => buildAddressRecipePlan(outgoingSelection.selected_recipe!, scopedFilters), baseFilters: filters, dateScope, maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count, maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe, deps: runtimeDeps }); incomingResult = incomingExecution.result; outgoingResult = outgoingExecution.result; pushUnique(executedPrimitives, step.primitive_id); probeResults.push(...incomingExecution.probe_results, ...outgoingExecution.probe_results); for (const limitation of [...incomingExecution.query_limitations, ...outgoingExecution.query_limitations]) { pushUnique(queryLimitations, limitation); } if (incomingResult?.error) { pushReason(reasonCodes, "pilot_bidirectional_incoming_query_movements_mcp_error"); } if (outgoingResult?.error) { pushReason(reasonCodes, "pilot_bidirectional_outgoing_query_movements_mcp_error"); } if (incomingResult?.coverage_recovered_by_period_chunking) { pushReason(reasonCodes, "pilot_bidirectional_incoming_monthly_period_chunking_recovered_coverage"); } if (outgoingResult?.coverage_recovered_by_period_chunking) { pushReason(reasonCodes, "pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage"); } if (!incomingResult?.error || !outgoingResult?.error) { pushReason(reasonCodes, "pilot_bidirectional_query_movements_mcp_executed"); } } const sourceRowsSummary = summarizeBidirectionalValueFlowRows({ incomingResult, outgoingResult }); const derivedBidirectionalValueFlow = deriveBidirectionalValueFlow({ incomingResult, outgoingResult, counterparty, periodScope: dateScope, aggregationAxis }); if (derivedBidirectionalValueFlow) { pushReason(reasonCodes, "pilot_derived_bidirectional_value_flow_from_confirmed_rows"); if (aggregationAxis === "month" && derivedBidirectionalValueFlow.monthly_breakdown.length > 0) { pushReason(reasonCodes, "pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows"); } } const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: buildBidirectionalValueFlowConfirmedFacts(derivedBidirectionalValueFlow), inferredFacts: buildBidirectionalValueFlowInferredFacts(derivedBidirectionalValueFlow), unknownFacts: buildBidirectionalValueFlowUnknownFacts(dateScope, derivedBidirectionalValueFlow), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: valueFlowProfile.scope, dry_run: dryRun, mcp_execution_performed: executedPrimitives.length > 0, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: sourceRowsSummary, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: derivedBidirectionalValueFlow, query_limitations: queryLimitations, reason_codes: reasonCodes }; } const recipeIntent = valueFlowProfile.recipe_intent; const selection = recipeIntent ? selectAddressRecipe(recipeIntent, filters) : { selected_recipe: null }; 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: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "unsupported", pilot_scope: valueFlowProfile.scope, dry_run: dryRun, mcp_execution_performed: false, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: null, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Value-flow recipe is not available"], reason_codes: reasonCodes }; } pushReason( reasonCodes, valueFlowProfile.direction === "outgoing_supplier_payout" ? "pilot_supplier_payout_recipe_selected" : "pilot_customer_revenue_recipe_selected" ); 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; } const execution = await executeCoverageAwareValueFlowQuery({ primitiveId: step.primitive_id, recipePlanBuilder: (scopedFilters) => buildAddressRecipePlan(selection.selected_recipe!, scopedFilters), baseFilters: filters, dateScope, maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count, maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe, deps: runtimeDeps }); queryResult = execution.result; pushUnique(executedPrimitives, step.primitive_id); probeResults.push(...execution.probe_results); for (const limitation of execution.query_limitations) { pushUnique(queryLimitations, limitation); } if (queryResult?.error) { pushReason(reasonCodes, "pilot_query_movements_mcp_error"); } else { pushReason(reasonCodes, "pilot_query_movements_mcp_executed"); } if (queryResult?.coverage_recovered_by_period_chunking) { pushReason(reasonCodes, "pilot_monthly_period_chunking_recovered_coverage"); } } const sourceRowsSummary = queryResult ? summarizeValueFlowRows(queryResult) : null; if (planner.selected_chain_id === "value_flow_ranking" && rankingNeed) { const derivedRankedValueFlow = deriveRankedValueFlow(queryResult, { organizationScope, periodScope: dateScope, direction: valueFlowProfile.direction, rankingNeed }); if (derivedRankedValueFlow) { pushReason(reasonCodes, "pilot_derived_ranked_value_flow_from_confirmed_rows"); } const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: buildRankedValueFlowConfirmedFacts(derivedRankedValueFlow), inferredFacts: buildRankedValueFlowInferredFacts(derivedRankedValueFlow), unknownFacts: buildRankedValueFlowUnknownFacts(dateScope, derivedRankedValueFlow), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: valueFlowProfile.scope, dry_run: dryRun, mcp_execution_performed: executedPrimitives.length > 0, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: sourceRowsSummary, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_ranked_value_flow: derivedRankedValueFlow, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } const derivedValueFlow = deriveValueFlow( queryResult, counterparty, dateScope, valueFlowProfile.direction, aggregationAxis ); if (derivedValueFlow) { pushReason(reasonCodes, "pilot_derived_value_flow_from_confirmed_rows"); if (aggregationAxis === "month" && derivedValueFlow.monthly_breakdown.length > 0) { pushReason(reasonCodes, "pilot_derived_value_flow_monthly_breakdown_from_confirmed_rows"); } } const evidence = resolveAssistantMcpDiscoveryEvidence({ plan: planner.discovery_plan, probeResults, confirmedFacts: queryResult ? buildValueFlowConfirmedFacts(queryResult, counterparty, valueFlowProfile.direction) : [], inferredFacts: buildValueFlowInferredFacts(derivedValueFlow), unknownFacts: buildValueFlowUnknownFacts(dateScope, valueFlowProfile.direction, derivedValueFlow), sourceRowsSummary, queryLimitations, recommendedNextProbe: "explain_evidence_basis" }); return { schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION, policy_owner: "assistantMcpDiscoveryPilotExecutor", pilot_status: "executed", pilot_scope: valueFlowProfile.scope, dry_run: dryRun, mcp_execution_performed: executedPrimitives.length > 0, executed_primitives: executedPrimitives, skipped_primitives: skippedPrimitives, probe_results: probeResults, evidence, source_rows_summary: sourceRowsSummary, derived_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_ranked_value_flow: null, derived_value_flow: derivedValueFlow, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } let queryResult: AddressMcpQueryExecutorResult | null = null; const filters = buildLifecycleFilters(planner); const selection = 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: 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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: null, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: ["Lifecycle recipe is not available"], reason_codes: reasonCodes }; } const recipePlan = 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 runtimeDeps.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 = 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: 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_metadata_surface: null, derived_entity_resolution: null, derived_activity_period: derivedActivityPeriod, derived_value_flow: null, derived_bidirectional_value_flow: null, query_limitations: queryLimitations, reason_codes: reasonCodes }; } export type AssistantMcpDiscoveryPilotMetadataResult = AddressMcpMetadataRowsResult;