1949 lines
88 KiB
TypeScript
1949 lines
88 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
||
import { planAssistantMcpDiscovery } from "../src/services/assistantMcpDiscoveryPlanner";
|
||
import { executeAssistantMcpDiscoveryPilot } from "../src/services/assistantMcpDiscoveryPilotExecutor";
|
||
|
||
function buildDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||
return {
|
||
executeAddressMcpQuery: vi.fn(async () => ({
|
||
fetched_rows: rows.length,
|
||
matched_rows: error ? 0 : rows.length,
|
||
raw_rows: rows,
|
||
rows: error ? [] : rows,
|
||
error
|
||
}))
|
||
};
|
||
}
|
||
|
||
function buildSequentialDeps(results: Array<{ rows: Array<Record<string, unknown>>; error?: string | null }>) {
|
||
const executeAddressMcpQuery = vi.fn(async () => {
|
||
const next = results.shift() ?? { rows: [] };
|
||
const rows = next.rows;
|
||
const error = next.error ?? null;
|
||
return {
|
||
fetched_rows: rows.length,
|
||
matched_rows: error ? 0 : rows.length,
|
||
raw_rows: rows,
|
||
rows: error ? [] : rows,
|
||
error
|
||
};
|
||
});
|
||
return { executeAddressMcpQuery };
|
||
}
|
||
|
||
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||
return {
|
||
executeAddressMcpMetadata: vi.fn(async () => ({
|
||
fetched_rows: error ? 0 : rows.length,
|
||
raw_rows: error ? [] : rows,
|
||
rows: error ? [] : rows,
|
||
error
|
||
}))
|
||
};
|
||
}
|
||
|
||
describe("assistant MCP discovery pilot executor", () => {
|
||
it("executes only the lifecycle query_documents primitive through injected MCP deps", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["SVK"]
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Период: "2020-01-15T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY_FIRST", Контрагент: "SVK" },
|
||
{ Период: "2023-12-20T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY", Контрагент: "SVK" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["query_documents"]);
|
||
expect(result.skipped_primitives).toEqual(["resolve_entity_reference", "probe_coverage", "explain_evidence_basis"]);
|
||
expect(result.evidence.evidence_status).toBe("confirmed");
|
||
expect(result.evidence.confirmed_facts[0]).toContain("SVK");
|
||
expect(result.evidence.inferred_facts[0]).toContain("may be inferred");
|
||
expect(result.evidence.unknown_facts).toContain("Legal registration date is not proven by this MCP discovery pilot");
|
||
expect(result.source_rows_summary).toBe("2 MCP document rows fetched, 2 matched lifecycle scope");
|
||
expect(result.derived_activity_period).toEqual({
|
||
first_activity_date: "2020-01-15",
|
||
latest_activity_date: "2023-12-20",
|
||
matched_rows: 2,
|
||
duration_total_months: 47,
|
||
duration_years: 3,
|
||
duration_months_remainder: 11,
|
||
duration_human_ru: "3 года 11 месяцев",
|
||
inference_basis: "first_and_latest_confirmed_1c_activity_rows"
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_query_documents_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_activity_period_from_confirmed_rows");
|
||
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||
const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0];
|
||
expect(String(call?.query ?? "")).toContain("Документ.ПоступлениеНаРасчетныйСчет");
|
||
expect(call?.limit).toBeGreaterThan(0);
|
||
});
|
||
|
||
it("does not execute MCP when dry-run still needs clarification", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["SVK"]
|
||
}
|
||
});
|
||
const deps = buildDeps([]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("skipped_needs_clarification");
|
||
expect(result.mcp_execution_performed).toBe(false);
|
||
expect(result.evidence.evidence_status).toBe("insufficient");
|
||
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it("executes business overview as a bounded multi-probe bridge over money flow and activity evidence", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "all_time_scope",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" },
|
||
{ Period: "2020-02-15T00:00:00", Amount: 80000, Counterparty: "Клиент Б" },
|
||
{ Period: "2021-03-15T00:00:00", Amount: 220000, Counterparty: "Клиент А" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-20T00:00:00", Amount: 150000, Counterparty: "Поставщик А" },
|
||
{ Period: "2021-03-20T00:00:00", Amount: 50000, Counterparty: "Поставщик Б" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "DOC_TYPE_DOCS", AccountDt: "Списание с расчетного счета", Amount: 12 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "DOC_TYPE_DOCS", AccountDt: "Поступление на расчетный счет", Amount: 8 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "SECTION_DT_OPS", AccountDt: "60", Amount: 9 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "SECTION_KT_OPS", AccountDt: "62", Amount: 6 }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_TOTAL", Amount: 412 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_CUSTOMER_ACTIVE", Amount: 145 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_SUPPLIER_ACTIVE", Amount: 94 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_MIXED_ACTIVE", Amount: 23 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CP_ACTIVE_UNION", Amount: 216 }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CT_TOTAL", Amount: 520 },
|
||
{ Period: "2020-01-01T00:00:00", Registrator: "CT_USED", Amount: 148 }
|
||
]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(planner.planner_status).toBe("ready_for_execution");
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("business_overview_route_template_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["query_movements", "query_documents"]);
|
||
expect(result.skipped_primitives).toEqual([
|
||
"aggregate_by_axis",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
]);
|
||
expect(result.derived_business_overview).toMatchObject({
|
||
organization_scope: "ООО Альтернатива Плюс",
|
||
incoming_customer_revenue: {
|
||
total_amount: 420000,
|
||
rows_with_amount: 3
|
||
},
|
||
outgoing_supplier_payout: {
|
||
total_amount: 200000,
|
||
rows_with_amount: 2
|
||
},
|
||
net_amount: 220000,
|
||
net_direction: "net_incoming"
|
||
});
|
||
expect(result.derived_business_overview?.top_customers[0]).toMatchObject({
|
||
axis_value: "Клиент А",
|
||
total_amount: 340000
|
||
});
|
||
expect(result.derived_business_overview?.top_suppliers[0]).toMatchObject({
|
||
axis_value: "Поставщик А",
|
||
total_amount: 150000
|
||
});
|
||
expect(result.derived_business_overview?.yearly_breakdown).toMatchObject([
|
||
{
|
||
year_bucket: "2020",
|
||
incoming_total_amount: 200000,
|
||
outgoing_total_amount: 150000,
|
||
net_amount: 50000
|
||
},
|
||
{
|
||
year_bucket: "2021",
|
||
incoming_total_amount: 220000,
|
||
outgoing_total_amount: 50000,
|
||
net_amount: 170000
|
||
}
|
||
]);
|
||
expect(result.derived_business_overview?.activity_period?.duration_total_months).toBe(11);
|
||
expect(result.derived_business_overview?.document_activity_profile).toMatchObject({
|
||
rows_matched: 4,
|
||
total_document_type_count: 20,
|
||
total_account_section_operations: 15
|
||
});
|
||
expect(result.derived_business_overview?.document_activity_profile?.top_document_types[0]).toMatchObject({
|
||
document_type: "Списание с расчетного счета",
|
||
count: 12,
|
||
share_pct: 60
|
||
});
|
||
expect(result.derived_business_overview?.document_activity_profile?.top_account_sections[0]).toMatchObject({
|
||
account_section: "60",
|
||
operation_count: 9,
|
||
share_pct: 60
|
||
});
|
||
expect(result.derived_business_overview?.counterparty_profile).toMatchObject({
|
||
rows_matched: 5,
|
||
total_counterparties: 412,
|
||
active_counterparties: 216,
|
||
customer_only_count: 122,
|
||
supplier_only_count: 71,
|
||
mixed_role_count: 23,
|
||
other_or_inactive_count: 196
|
||
});
|
||
expect(result.derived_business_overview?.contract_usage_profile).toMatchObject({
|
||
rows_matched: 2,
|
||
total_contracts: 520,
|
||
used_contracts: 148,
|
||
unused_contracts: 372,
|
||
used_contract_share_pct: 28.46
|
||
});
|
||
expect(result.derived_business_overview?.vendor_procurement_quality).toMatchObject({
|
||
rows_with_amount: 2,
|
||
total_outgoing_amount: 200000,
|
||
top_outgoing_share_pct: 75,
|
||
top_non_financial_supplier_share_pct: 75,
|
||
financial_institution_leads_outgoing_cash: false,
|
||
supplier_only_count: 71,
|
||
mixed_role_count: 23,
|
||
used_contracts: 148,
|
||
total_contracts: 520,
|
||
used_contract_share_pct: 28.46,
|
||
evidence_status: "reviewed_procurement_concentration",
|
||
inference_basis: "supplier_payout_concentration_counterparty_contract_profile_confirmed_1c_rows"
|
||
});
|
||
expect(result.derived_business_overview?.vendor_procurement_quality?.top_outgoing_counterparty).toMatchObject({
|
||
axis_value: "Поставщик А",
|
||
total_amount: 150000
|
||
});
|
||
expect(result.derived_business_overview?.missing_proof_families).toEqual(
|
||
expect.arrayContaining([
|
||
expect.objectContaining({
|
||
family: "accounting_profit_margin",
|
||
current_status: "reviewed_route_not_wired",
|
||
current_supported_evidence: null
|
||
})
|
||
])
|
||
);
|
||
expect(result.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
|
||
"vendor_risk_procurement_quality"
|
||
);
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("В 1С подтверждены входящие поступления");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Самый крупный подтвержденный поставщик");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Годовая раскладка операционного денежного потока");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Профиль операционной активности");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Профиль контрагентской базы");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Договорной профиль");
|
||
expect(result.evidence.inferred_facts.join("\n")).toContain("procurement concentration proxy");
|
||
expect(result.evidence.inferred_facts.join("\n")).toContain("Самый сильный год по подтвержденным входящим поступлениям: 2021");
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Прибыль и маржа этим бизнес-обзором не подтверждены: нужны себестоимость, расходы и закрывающие документы."
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_top_suppliers_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_yearly_operating_breakdown_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_document_activity_profile_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_counterparty_profile_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_contract_usage_profile_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_vendor_procurement_quality_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_vendor_procurement_quality_reviewed_procurement_concentration");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_missing_proof_families_recorded");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(6);
|
||
});
|
||
|
||
it("marks bank-like counterparties in business-overview rankings before evidence wording", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "all_time_scope",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 1200000, Counterparty: "СБЕРБАНК, ПАО", ВидОперации: "Возврат от поставщика", Договор: "Депозитный договор" },
|
||
{ Period: "2020-02-15T00:00:00", Amount: 800000, Counterparty: "Группа СВК" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-20T00:00:00", Amount: 650000, Counterparty: "СБЕРБАНК, ПАО", ВидОперации: "ПрочееСписание", НазначениеПлатежа: "Комиссия банка за ведение счета" },
|
||
{ Period: "2020-03-20T00:00:00", Amount: 50000, Counterparty: "ООО Поставщик" }
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.derived_business_overview?.top_customers[0]).toMatchObject({
|
||
axis_value: "СБЕРБАНК, ПАО",
|
||
counterparty_role_hint: "bank_or_financial_institution",
|
||
financial_flow_hint: "loan_or_credit"
|
||
});
|
||
expect(result.derived_business_overview?.top_customers[1]).toMatchObject({
|
||
axis_value: "Группа СВК",
|
||
counterparty_role_hint: "ordinary_counterparty"
|
||
});
|
||
expect(result.derived_business_overview?.top_suppliers[0]).toMatchObject({
|
||
axis_value: "СБЕРБАНК, ПАО",
|
||
counterparty_role_hint: "bank_or_financial_institution",
|
||
financial_flow_hint: "bank_fee_or_service"
|
||
});
|
||
const confirmedFacts = result.evidence.confirmed_facts.join("\n");
|
||
const inferredFacts = result.evidence.inferred_facts.join("\n");
|
||
expect(confirmedFacts).toContain("Крупнейший входящий денежный источник");
|
||
expect(confirmedFacts).toContain("Крупнейший небанковский входящий контрагент");
|
||
expect(confirmedFacts).toContain("Крупнейший получатель исходящих денег");
|
||
expect(confirmedFacts).toContain("кредитный/заемный признак");
|
||
expect(confirmedFacts).toContain("банковской комиссии/услуг банка");
|
||
expect(confirmedFacts).not.toContain("Самый крупный подтвержденный клиент в проверенном срезе: СБЕРБАНК");
|
||
expect(confirmedFacts).not.toContain("Самый крупный подтвержденный поставщик/получатель исходящих платежей в проверенном срезе: СБЕРБАНК");
|
||
expect(inferredFacts).toContain("outgoing cash concentration proxy");
|
||
});
|
||
|
||
it("adds a checked VAT/tax family to business overview only when an explicit period is available", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Регистратор: "VAT_BOOK_SALES", СчетДт: "68.02", Сумма: 40000 },
|
||
{ Регистратор: "VAT_BOOK_PURCHASES", СчетДт: "19", Сумма: 12000 }
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" },
|
||
{ Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" }
|
||
]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_business_overview?.tax_position).toMatchObject({
|
||
period_scope: "2020",
|
||
rows_matched: 2,
|
||
rows_with_amount: 2,
|
||
sales_vat_amount: 40000,
|
||
purchase_vat_amount: 12000,
|
||
net_vat_amount: 28000,
|
||
net_vat_direction: "vat_to_pay"
|
||
});
|
||
expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({
|
||
period_scope: "2020",
|
||
sales_rows_with_amount: 1,
|
||
purchase_rows_with_amount: 1,
|
||
sales_revenue: 200000,
|
||
purchase_cost_proxy: 120000,
|
||
gross_spread_proxy: 80000,
|
||
margin_to_revenue_pct: 40
|
||
});
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("tax_position");
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("profit_margin");
|
||
expect(result.derived_business_overview?.missing_signal_families).toContain("accounting_profit_margin");
|
||
expect(result.derived_business_overview?.missing_proof_families).toEqual(
|
||
expect.arrayContaining([
|
||
expect.objectContaining({
|
||
family: "accounting_profit_margin",
|
||
current_status: "proxy_only_currently",
|
||
current_supported_evidence: "trading_margin_proxy_from_sales_and_purchase_document_rows"
|
||
})
|
||
])
|
||
);
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("НДС-позиция за 2020 подтверждена");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Торговый margin proxy за 2020");
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain("Чистая прибыль");
|
||
expect(result.evidence.unknown_facts.join("\n")).not.toContain("Налоговая/VAT-позиция этим бизнес-обзором не подтверждена");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_tax_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_tax_position_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_trading_margin_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_trading_margin_proxy_from_confirmed_rows");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||
const taxCall = deps.executeAddressMcpQuery.mock.calls[2]?.[0];
|
||
const tradingMarginCall = deps.executeAddressMcpQuery.mock.calls[10]?.[0];
|
||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПродаж");
|
||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПокупок");
|
||
expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.РеализацияТоваровУслуг.Товары");
|
||
expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.ПоступлениеТоваровУслуг.Товары");
|
||
});
|
||
|
||
it("keeps negative trading-margin proxy signed instead of reporting an absolute spread", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-03-01T00:00:00", Amount: 100000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" },
|
||
{ Period: "2020-02-01T00:00:00", Amount: 150000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" }
|
||
]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({
|
||
sales_revenue: 100000,
|
||
purchase_cost_proxy: 150000,
|
||
gross_spread_proxy: -50000,
|
||
gross_spread_proxy_human_ru: "-50 000 руб.",
|
||
margin_to_revenue_pct: -50
|
||
});
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("валовый спред proxy -50 000 руб.");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("не чистая прибыль");
|
||
});
|
||
|
||
it("adds a checked debt-position family to business overview only as an as-of-date snapshot", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 70000, Counterparty: "Клиент А" },
|
||
{ Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 40000, Counterparty: "Поставщик А" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 90000, Counterparty: "Клиент А", Contract: "Договор А от 10.02.2019" },
|
||
{ Period: "2020-12-31T00:00:00", Amount: 30000, Counterparty: "Клиент Б", Contract: "Договор Б от 15.03.2020" }
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||
]
|
||
},
|
||
{ rows: [] }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_business_overview?.debt_position).toMatchObject({
|
||
as_of_date: "2020-12-31",
|
||
receivables: {
|
||
total_amount: 100000,
|
||
rows_with_amount: 2
|
||
},
|
||
payables: {
|
||
total_amount: 40000,
|
||
rows_with_amount: 1
|
||
},
|
||
net_debt_position_amount: 60000,
|
||
net_debt_position_direction: "net_receivable"
|
||
});
|
||
expect(result.derived_business_overview?.debt_position?.receivables.top_counterparties[0]).toMatchObject({
|
||
axis_value: "Клиент А",
|
||
total_amount: 70000
|
||
});
|
||
expect(result.derived_business_overview?.debt_open_settlement_quality).toMatchObject({
|
||
as_of_date: "2020-12-31",
|
||
rows_with_amount: 2,
|
||
gross_open_amount: 120000,
|
||
unique_counterparties: 2,
|
||
unique_contracts: 2,
|
||
concentration_top_contract_pct: 75
|
||
});
|
||
expect(result.derived_business_overview?.debt_open_settlement_quality?.age_signal).toMatchObject({
|
||
contracts_with_start_date: 2,
|
||
oldest_start_date: "2019-02-10",
|
||
latest_start_date: "2020-03-15",
|
||
max_age_days: 690
|
||
});
|
||
expect(result.derived_business_overview?.debt_staleness_risk_proxy).toMatchObject({
|
||
as_of_date: "2020-12-31",
|
||
oldest_contract_start_date: "2019-02-10",
|
||
max_contract_age_days: 690,
|
||
top_contract_amount: 90000,
|
||
top_contract_share_pct: 75,
|
||
risk_band: "high"
|
||
});
|
||
expect(result.derived_business_overview?.debt_open_settlement_quality?.age_signal?.top_aged_contracts[0]).toMatchObject({
|
||
contract: "Договор А от 10.02.2019",
|
||
start_date: "2019-02-10",
|
||
age_days: 690,
|
||
total_amount: 90000
|
||
});
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_position");
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_open_settlement_quality");
|
||
expect(result.derived_business_overview?.missing_signal_families).toContain("debt_due_date_aging_quality");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Долговая позиция на 2020-12-31");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Качество открытых расчетов на 2020-12-31");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Возрастной сигнал открытых расчетов");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Staleness risk proxy открытых расчетов");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("высокая зона внимания");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("не due-date анализ");
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain("due-date");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_debt_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_position_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_open_contracts_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_open_settlement_quality_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_age_signal_from_contract_dates");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_staleness_risk_proxy_from_confirmed_rows");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||
const receivablesCall = deps.executeAddressMcpQuery.mock.calls[4]?.[0];
|
||
const payablesCall = deps.executeAddressMcpQuery.mock.calls[5]?.[0];
|
||
const openContractsCall = deps.executeAddressMcpQuery.mock.calls[6]?.[0];
|
||
expect(String(receivablesCall?.query ?? "")).toContain("62");
|
||
expect(String(payablesCall?.query ?? "")).toContain("60");
|
||
expect(String(openContractsCall?.query ?? "")).toContain("СуммаРазвернутыйОстатокКт");
|
||
});
|
||
|
||
it("checks debt due-date aging with contract payment terms before claiming overdue debt", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "debt_due_date_boundary",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "due_date_aging",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_overdue_claim"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "debt_due_date_boundary",
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 100000, Counterparty: "Клиент А", Contract: "Договор А от 10.02.2019" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{
|
||
Period: "2020-12-31T00:00:00",
|
||
Amount: 100000,
|
||
Counterparty: "Клиент А",
|
||
Contract: "Договор А от 10.02.2019",
|
||
ДокументРасчетов: "Реализация товаров от 10.03.2020",
|
||
УстановленСрокОплаты: false,
|
||
СрокОплаты: 0
|
||
}
|
||
]
|
||
},
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.derived_business_overview?.debt_due_date_aging).toMatchObject({
|
||
as_of_date: "2020-12-31",
|
||
rows_with_amount: 1,
|
||
gross_open_amount: 100000,
|
||
rows_with_payment_terms: 0,
|
||
rows_without_payment_terms: 1,
|
||
overdue_rows: 0,
|
||
evidence_status: "no_payment_terms_configured"
|
||
});
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("debt_due_date_aging_quality");
|
||
expect(result.derived_business_overview?.missing_proof_families.map((item) => item.family)).not.toContain(
|
||
"debt_due_date_aging_quality"
|
||
);
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("срок оплаты не установлен");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Подтвержденной просрочки");
|
||
expect(result.evidence.unknown_facts.join("\n")).not.toContain("due-date aging этим бизнес-обзором не подтверждены");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_debt_due_date_aging_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_due_date_aging_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_due_date_aging_no_payment_terms_configured");
|
||
const dueDateCall = deps.executeAddressMcpQuery.mock.calls[7]?.[0];
|
||
expect(String(dueDateCall?.query ?? "")).toContain("УстановленСрокОплаты");
|
||
expect(String(dueDateCall?.query ?? "")).toContain("СрокОплаты");
|
||
});
|
||
|
||
it("adds a checked inventory-position family to business overview only as an as-of-date snapshot", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
dataNeedGraph: {
|
||
schema_version: "assistant_data_need_graph_v1",
|
||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||
subject_candidates: [],
|
||
business_fact_family: "business_overview",
|
||
action_family: "broad_evaluation",
|
||
aggregation_need: null,
|
||
time_scope_need: "explicit_period",
|
||
comparison_need: null,
|
||
ranking_need: null,
|
||
proof_expectation: "bounded_inference",
|
||
clarification_gaps: [],
|
||
decomposition_candidates: [
|
||
"collect_scoped_movements",
|
||
"aggregate_checked_amounts",
|
||
"aggregate_ranked_axis_values",
|
||
"fetch_supporting_documents",
|
||
"probe_coverage",
|
||
"explain_evidence_basis"
|
||
],
|
||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "business_overview",
|
||
asked_action_family: "broad_evaluation",
|
||
explicit_organization_scope: "ООО Тест",
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{ rows: [{ Period: "2020-01-15T00:00:00", Amount: 120000, Counterparty: "Клиент А" }] },
|
||
{ rows: [{ Period: "2020-01-20T00:00:00", Amount: 50000, Counterparty: "Поставщик А" }] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{ rows: [] },
|
||
{
|
||
rows: [
|
||
{ Period: "2020-12-31T00:00:00", Amount: 250000, Quantity: 10, Item: "Товар А" },
|
||
{ Period: "2020-12-31T00:00:00", Amount: 50000, Quantity: 5, Item: "Товар Б" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-10T00:00:00", Amount: 200000, Quantity: 8, Item: "Товар А" },
|
||
{ Period: "2020-11-01T00:00:00", Amount: 50000, Quantity: 2, Item: "Товар Б" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-03-01T00:00:00", Amount: 600000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" },
|
||
{ Period: "2020-02-01T00:00:00", Amount: 240000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" }
|
||
]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_business_overview?.inventory_position).toMatchObject({
|
||
as_of_date: "2020-12-31",
|
||
rows_matched: 2,
|
||
rows_with_amount: 2,
|
||
rows_with_quantity: 2,
|
||
total_amount: 300000,
|
||
total_quantity: 15,
|
||
aging_signal: {
|
||
rows_matched: 2,
|
||
rows_with_purchase_date: 2,
|
||
oldest_purchase_date: "2020-01-10",
|
||
latest_purchase_date: "2020-11-01",
|
||
max_age_days: 356
|
||
}
|
||
});
|
||
expect(result.derived_business_overview?.inventory_position?.top_items[0]).toMatchObject({
|
||
item: "Товар А",
|
||
total_amount: 250000,
|
||
total_quantity: 10
|
||
});
|
||
expect(result.derived_business_overview?.inventory_turnover_proxy).toMatchObject({
|
||
period_scope: "2020",
|
||
as_of_date: "2020-12-31",
|
||
sales_revenue: 600000,
|
||
inventory_amount: 300000,
|
||
sales_to_stock_amount_ratio: 2,
|
||
stock_to_sales_revenue_pct: 50
|
||
});
|
||
expect(result.derived_business_overview?.inventory_staleness_risk_proxy).toMatchObject({
|
||
period_scope: "2020",
|
||
as_of_date: "2020-12-31",
|
||
oldest_purchase_date: "2020-01-10",
|
||
max_purchase_age_days: 356,
|
||
sales_to_stock_amount_ratio: 2,
|
||
risk_band: "watch"
|
||
});
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_position");
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_turnover_quality");
|
||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("inventory_liquidity_quality");
|
||
expect(result.derived_business_overview?.missing_signal_families).toContain("inventory_reserve_liquidation_quality");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Складской срез на 2020-12-31");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Оборотный proxy склада за 2020");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("sales-to-stock ratio 2x");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Staleness risk proxy склада");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("зона наблюдения");
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain("Резервы");
|
||
expect(result.reason_codes).toContain("pilot_business_overview_inventory_query_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_position_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_turnover_proxy_from_confirmed_rows");
|
||
expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_staleness_risk_proxy_from_confirmed_rows");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||
const inventoryCall = deps.executeAddressMcpQuery.mock.calls[7]?.[0];
|
||
expect(inventoryCall?.account_scope).toContain("41.01");
|
||
});
|
||
|
||
it("uses the explicit selected chain id when choosing the movement pilot scope", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence"
|
||
}
|
||
});
|
||
const deps = buildDeps([{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" }]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(
|
||
{
|
||
...planner,
|
||
reason_codes: planner.reason_codes.filter((code) => !code.startsWith("planner_selected_"))
|
||
},
|
||
deps
|
||
);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1");
|
||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||
});
|
||
|
||
it("executes generic document evidence through query_documents", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Period: "2020-01-15T00:00:00", Counterparty: "SVK", Registrar: "Doc1" },
|
||
{ Period: "2020-03-20T00:00:00", Counterparty: "SVK", Registrar: "Doc2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1");
|
||
expect(result.executed_primitives).toEqual(["query_documents"]);
|
||
expect(result.evidence.confirmed_facts).toContain("В 1С найдены строки документов по контрагенту SVK за 2020.");
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Срез документов по контрагенту SVK за 2020 ограничен только подтвержденными строками документов, найденными этим поиском."
|
||
);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Полный исторический срез документов по контрагенту SVK вне периода 2020 этим поиском не подтвержден."
|
||
);
|
||
expect(result.source_rows_summary).toBe("2 MCP document rows fetched, 2 matched document scope");
|
||
});
|
||
|
||
it("executes generic movement evidence through query_movements without deriving turnover totals", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK", Registrar: "Move1" },
|
||
{ Period: "2020-03-20T00:00:00", Amount: "900,25", Counterparty: "SVK", Registrar: "Move2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_movement_evidence_query_movements_v1");
|
||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||
expect(result.derived_value_flow).toBeNull();
|
||
expect(result.derived_bidirectional_value_flow).toBeNull();
|
||
expect(result.evidence.confirmed_facts).toContain("В 1С найдены строки движений по контрагенту SVK за 2020.");
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Срез движений по контрагенту SVK за 2020 ограничен только подтвержденными строками движений, найденными этим поиском."
|
||
);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Полный исторический срез движений по контрагенту SVK вне периода 2020 этим поиском не подтвержден."
|
||
);
|
||
expect(result.source_rows_summary).toBe("2 MCP movement rows fetched, 2 matched movement scope");
|
||
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||
const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0];
|
||
expect(String(call?.query ?? "")).toContain("Документ.СписаниеСРасчетногоСчета");
|
||
expect(String(call?.query ?? "")).toContain("Документ.ПоступлениеНаРасчетныйСчет");
|
||
expect(call?.limit).toBeGreaterThan(0);
|
||
});
|
||
|
||
it("executes inspect_1c_metadata and derives a confirmed metadata surface", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_documents",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Документ.СчетФактураВыданный",
|
||
MetaType: "Документ",
|
||
attributes: [{ Name: "Дата" }, { Name: "Организация" }]
|
||
},
|
||
{
|
||
FullName: "Документ.СчетФактураПолученный",
|
||
MetaType: "Документ",
|
||
attributes: [{ Name: "Контрагент" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("metadata_inspection_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["inspect_1c_metadata"]);
|
||
expect(result.evidence.evidence_status).toBe("confirmed");
|
||
expect(result.source_rows_summary).toBe("2 MCP metadata rows fetched");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
requested_meta_types: ["Документ"],
|
||
matched_rows: 2,
|
||
available_entity_sets: ["Документ"],
|
||
matched_objects: ["Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный"],
|
||
selected_entity_set: "Документ",
|
||
selected_surface_objects: ["Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный"],
|
||
downstream_route_family: "document_evidence",
|
||
recommended_next_primitive: "query_documents",
|
||
ambiguity_detected: false,
|
||
ambiguity_entity_sets: [],
|
||
available_fields: ["Дата", "Организация", "Контрагент"],
|
||
inference_basis: "confirmed_1c_metadata_surface_rows"
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_inspect_1c_metadata_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_metadata_surface_from_confirmed_rows");
|
||
expect(deps.executeAddressMcpMetadata).toHaveBeenCalledTimes(1);
|
||
expect(deps.executeAddressMcpMetadata.mock.calls[0]?.[0]).toMatchObject({
|
||
meta_type: ["Документ"],
|
||
name_mask: "НДС"
|
||
});
|
||
});
|
||
|
||
it("executes catalog drilldown through a narrowed metadata probe seeded from the confirmed surface object", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
metadataSurface: {
|
||
selected_entity_set: "Catalog",
|
||
selected_surface_objects: ["Catalog.Counterparties"],
|
||
downstream_route_family: "catalog_drilldown",
|
||
route_family_selection_basis: "selected_entity_set",
|
||
recommended_next_primitive: "drilldown_related_objects",
|
||
ambiguity_detected: false,
|
||
ambiguity_entity_sets: []
|
||
},
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_catalog",
|
||
unsupported_but_understood_family: "schema_surface"
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Catalog.Counterparties",
|
||
MetaType: "Catalog",
|
||
attributes: [{ Name: "Description" }]
|
||
},
|
||
{
|
||
FullName: "Catalog.Contracts",
|
||
MetaType: "Catalog",
|
||
attributes: [{ Name: "Owner" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("metadata_inspection_v1");
|
||
expect(result.executed_primitives).toEqual(["inspect_1c_metadata"]);
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "Counterparties",
|
||
requested_meta_types: ["Catalog"],
|
||
available_entity_sets: ["Catalog"],
|
||
selected_entity_set: "Catalog",
|
||
downstream_route_family: "catalog_drilldown",
|
||
recommended_next_primitive: "drilldown_related_objects"
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_catalog_drilldown_metadata_scope_seeded_from_surface_ref");
|
||
expect(deps.executeAddressMcpMetadata).toHaveBeenCalledTimes(1);
|
||
expect(deps.executeAddressMcpMetadata.mock.calls[0]?.[0]).toMatchObject({
|
||
meta_type: ["Catalog"],
|
||
name_mask: "Counterparties"
|
||
});
|
||
});
|
||
|
||
it("executes the full entity-resolution chain through the checked counterparty catalog slice", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
unsupported_but_understood_family: "entity_resolution"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" },
|
||
{ Counterparty: "СВК Логистика", CounterpartyRef: "Ref-2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("entity_resolution_search_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual([
|
||
"search_business_entity",
|
||
"resolve_entity_reference",
|
||
"probe_coverage"
|
||
]);
|
||
expect(result.skipped_primitives).toEqual([]);
|
||
expect(result.derived_entity_resolution).toMatchObject({
|
||
requested_entity: "Группа СВК",
|
||
resolution_status: "resolved",
|
||
resolved_entity: "Группа СВК",
|
||
resolved_reference: "Ref-1",
|
||
confidence: "high",
|
||
inference_basis: "catalog_counterparty_search_rows"
|
||
});
|
||
expect(result.evidence.confirmed_facts).toContain(
|
||
"В проверенном каталожном срезе 1С найден контрагент: Группа СВК"
|
||
);
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Пока проверено только заземление сущности по каталогу 1С; документы, движения и денежные показатели еще не проверялись"
|
||
);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Документы, движения и денежные показатели по этому контрагенту еще не проверялись; пока был только каталожный поиск"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_search_business_entity_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_resolve_entity_reference_from_catalog_rows");
|
||
expect(result.reason_codes).toContain("pilot_probe_coverage_executed_for_entity_resolution");
|
||
expect(result.reason_codes).toContain("pilot_entity_resolution_grounding_stable_for_downstream_probe");
|
||
expect(result.reason_codes).toContain("pilot_derived_entity_resolution_from_catalog_rows");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("keeps entity-resolution honest when several catalog candidates remain ambiguous", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["СВК"],
|
||
unsupported_but_understood_family: "entity_resolution"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Counterparty: "СВК-А", CounterpartyRef: "Ref-1" },
|
||
{ Counterparty: "СВК-Б", CounterpartyRef: "Ref-2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("entity_resolution_search_v1");
|
||
expect(result.executed_primitives).toEqual([
|
||
"search_business_entity",
|
||
"resolve_entity_reference",
|
||
"probe_coverage"
|
||
]);
|
||
expect(result.skipped_primitives).toEqual([]);
|
||
expect(result.derived_entity_resolution).toMatchObject({
|
||
requested_entity: "СВК",
|
||
resolution_status: "ambiguous",
|
||
resolved_entity: null,
|
||
ambiguity_candidates: ["СВК-А", "СВК-Б"],
|
||
confidence: "low"
|
||
});
|
||
expect(result.evidence.confirmed_facts).toEqual([]);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Точное заземление контрагента в 1С остается неоднозначным между вариантами: СВК-А, СВК-Б"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_resolve_entity_reference_requires_clarification");
|
||
expect(result.reason_codes).toContain("pilot_probe_coverage_executed_for_entity_resolution");
|
||
expect(result.reason_codes).toContain("pilot_entity_resolution_ambiguity_requires_clarification");
|
||
});
|
||
|
||
it("keeps metadata grounding ambiguous when several surface families compete", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_fields",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Документ.СчетФактураВыданный",
|
||
MetaType: "Документ",
|
||
attributes: [{ Name: "Дата" }]
|
||
},
|
||
{
|
||
FullName: "РегистрНакопления.НДСПокупок",
|
||
MetaType: "РегистрНакопления",
|
||
resources: [{ Name: "СуммаНДС" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
available_entity_sets: ["Документ", "РегистрНакопления"],
|
||
selected_entity_set: null,
|
||
downstream_route_family: null,
|
||
recommended_next_primitive: null,
|
||
ambiguity_detected: true,
|
||
ambiguity_entity_sets: ["Документ", "РегистрНакопления"]
|
||
});
|
||
expect(result.evidence.inferred_facts).toEqual([]);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления"
|
||
);
|
||
});
|
||
|
||
it("selects a downstream lane from dominant metadata surface objects when one family clearly prevails", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_surface",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Документ.СчетФактураВыданный",
|
||
MetaType: "Документ",
|
||
attributes: [{ Name: "Дата" }]
|
||
},
|
||
{
|
||
FullName: "Документ.СчетФактураПолученный",
|
||
MetaType: "Документ",
|
||
attributes: [{ Name: "Контрагент" }]
|
||
},
|
||
{
|
||
FullName: "РегистрНакопления.НДСПокупок",
|
||
MetaType: "РегистрНакопления",
|
||
resources: [{ Name: "СуммаНДС" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
available_entity_sets: ["Документ", "РегистрНакопления"],
|
||
selected_entity_set: null,
|
||
selected_surface_objects: [
|
||
"Документ.СчетФактураВыданный",
|
||
"Документ.СчетФактураПолученный"
|
||
],
|
||
surface_family_scores: {
|
||
document_evidence: 2,
|
||
movement_evidence: 1,
|
||
catalog_drilldown: 0
|
||
},
|
||
downstream_route_family: "document_evidence",
|
||
route_family_selection_basis: "dominant_surface_objects",
|
||
recommended_next_primitive: "query_documents",
|
||
ambiguity_detected: false,
|
||
ambiguity_entity_sets: []
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_selected_metadata_route_family_from_dominant_surface_objects");
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"A likely next checked lane may be inferred as document_evidence from the confirmed metadata surface"
|
||
);
|
||
expect(result.evidence.unknown_facts).not.toContain(
|
||
"Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления"
|
||
);
|
||
});
|
||
|
||
it("can break a weak metadata family tie by ranking surface objects against the requested scope", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_surface",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Document.НДССчетФактура",
|
||
MetaType: "Document",
|
||
attributes: [{ Name: "Дата" }]
|
||
},
|
||
{
|
||
FullName: "AccumulationRegister.BankOperations",
|
||
MetaType: "AccumulationRegister",
|
||
resources: [{ Name: "Amount" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
available_entity_sets: ["Document", "AccumulationRegister"],
|
||
selected_entity_set: null,
|
||
selected_surface_objects: ["Document.НДССчетФактура"],
|
||
surface_family_scores: {
|
||
document_evidence: 1,
|
||
movement_evidence: 1,
|
||
catalog_drilldown: 0
|
||
},
|
||
downstream_route_family: "document_evidence",
|
||
route_family_selection_basis: "dominant_surface_objects",
|
||
recommended_next_primitive: "query_documents",
|
||
ambiguity_detected: false,
|
||
ambiguity_entity_sets: [],
|
||
surface_object_ranking_applied: true
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_selected_metadata_route_family_from_surface_object_ranking");
|
||
});
|
||
|
||
it("keeps metadata ambiguity unresolved when surface-family scores are nearly tied", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_surface",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{ FullName: "Документ.A", MetaType: "Документ" },
|
||
{ FullName: "Документ.B", MetaType: "Документ" },
|
||
{ FullName: "Документ.C", MetaType: "Документ" },
|
||
{ FullName: "Документ.D", MetaType: "Документ" },
|
||
{ FullName: "РегистрНакопления.A", MetaType: "РегистрНакопления" },
|
||
{ FullName: "РегистрНакопления.B", MetaType: "РегистрНакопления" },
|
||
{ FullName: "РегистрНакопления.C", MetaType: "РегистрНакопления" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
selected_entity_set: null,
|
||
surface_family_scores: {
|
||
document_evidence: 4,
|
||
movement_evidence: 3,
|
||
catalog_drilldown: 0
|
||
},
|
||
downstream_route_family: null,
|
||
route_family_selection_basis: null,
|
||
recommended_next_primitive: null,
|
||
ambiguity_detected: true
|
||
});
|
||
expect(result.derived_metadata_surface?.ambiguity_entity_sets).toContain("Документ");
|
||
expect(result.derived_metadata_surface?.ambiguity_entity_sets).toContain("РегистрНакопления");
|
||
expect(result.reason_codes).not.toContain("pilot_selected_metadata_route_family_from_dominant_surface_objects");
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления"
|
||
);
|
||
});
|
||
|
||
it("infers metadata entity-set families from object names when meta type columns are absent", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_surface",
|
||
explicit_entity_candidates: ["НДС"]
|
||
}
|
||
});
|
||
const deps = buildMetadataDeps([
|
||
{
|
||
FullName: "Документ.СчетФактураВыданный",
|
||
attributes: [{ Name: "Дата" }]
|
||
},
|
||
{
|
||
FullName: "РегистрНакопления.НДСПокупок",
|
||
resources: [{ Name: "СуммаНДС" }]
|
||
},
|
||
{
|
||
FullName: "Справочник.КодыОперацийНДС"
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_metadata_surface).toMatchObject({
|
||
metadata_scope: "НДС",
|
||
available_entity_sets: ["Документ", "РегистрНакопления", "Справочник"],
|
||
selected_entity_set: null,
|
||
downstream_route_family: null,
|
||
recommended_next_primitive: null,
|
||
ambiguity_detected: true,
|
||
ambiguity_entity_sets: ["Документ", "РегистрНакопления", "Справочник"]
|
||
});
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Exact downstream metadata surface remains ambiguous across: Документ, РегистрНакопления, Справочник"
|
||
);
|
||
});
|
||
|
||
it("executes value-flow query_movements and derives a guarded turnover sum", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK" },
|
||
{ Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_value_flow_query_movements_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||
expect(result.skipped_primitives).toEqual(["resolve_entity_reference", "aggregate_by_axis", "probe_coverage"]);
|
||
expect(result.evidence.evidence_status).toBe("confirmed");
|
||
expect(result.evidence.confirmed_facts[0]).toContain("value-flow rows");
|
||
expect(result.source_rows_summary).toBe("2 MCP value-flow rows fetched, 2 matched value-flow scope");
|
||
expect(result.derived_value_flow).toMatchObject({
|
||
counterparty: "SVK",
|
||
period_scope: "2020",
|
||
rows_matched: 2,
|
||
rows_with_amount: 2,
|
||
total_amount: 3750.5,
|
||
coverage_limited_by_probe_limit: false,
|
||
first_movement_date: "2020-01-15",
|
||
latest_movement_date: "2020-02-20",
|
||
inference_basis: "sum_of_confirmed_1c_value_flow_rows"
|
||
});
|
||
expect(result.reason_codes).toContain("pilot_query_movements_mcp_executed");
|
||
expect(result.reason_codes).toContain("pilot_derived_value_flow_from_confirmed_rows");
|
||
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||
const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0];
|
||
expect(String(call?.query ?? "")).toContain("ПоступлениеНаРасчетныйСчет");
|
||
expect(call?.limit).toBeGreaterThan(0);
|
||
});
|
||
|
||
it("executes supplier payout query_movements and derives a guarded outgoing payment sum", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Period: "2020-03-15T00:00:00", Amount: 4100, Counterparty: "SVK" },
|
||
{ Period: "2020-04-20T00:00:00", Amount: "900,25", Counterparty: "SVK" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_supplier_payout_query_movements_v1");
|
||
expect(result.derived_value_flow).toMatchObject({
|
||
value_flow_direction: "outgoing_supplier_payout",
|
||
counterparty: "SVK",
|
||
period_scope: "2020",
|
||
rows_matched: 2,
|
||
rows_with_amount: 2,
|
||
total_amount: 5000.25,
|
||
coverage_limited_by_probe_limit: false,
|
||
first_movement_date: "2020-03-15",
|
||
latest_movement_date: "2020-04-20"
|
||
});
|
||
expect(result.evidence.confirmed_facts[0]).toContain("supplier-payout rows");
|
||
expect(result.evidence.inferred_facts[0]).toContain("supplier-payout total");
|
||
expect(result.evidence.unknown_facts).toContain("Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot");
|
||
expect(result.reason_codes).toContain("pilot_supplier_payout_recipe_selected");
|
||
|
||
const call = deps.executeAddressMcpQuery.mock.calls[0]?.[0];
|
||
expect(String(call?.query ?? "")).toContain("СписаниеСРасчетногоСчета");
|
||
});
|
||
|
||
it("marks value-flow coverage as limited when the probe row limit is reached", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow"
|
||
}
|
||
});
|
||
const rows = Array.from({ length: 200 }, (_, index) => ({
|
||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||
Amount: 10,
|
||
Counterparty: "SVK"
|
||
}));
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, buildDeps(rows));
|
||
|
||
expect(result.derived_value_flow?.coverage_limited_by_probe_limit).toBe(true);
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Complete requested-period coverage is not proven by the available checked rows"
|
||
);
|
||
});
|
||
|
||
it("recovers yearly value-flow coverage by splitting a limited broad probe into monthly subprobes", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow"
|
||
}
|
||
});
|
||
const broadRows = Array.from({ length: 200 }, (_, index) => ({
|
||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||
Amount: 10,
|
||
Counterparty: "SVK"
|
||
}));
|
||
const monthlyResults = Array.from({ length: 12 }, (_, index) => ({
|
||
rows: [
|
||
{
|
||
Period: `2020-${String(index + 1).padStart(2, "0")}-05T00:00:00`,
|
||
Amount: (index + 1) * 100,
|
||
Counterparty: "SVK"
|
||
}
|
||
]
|
||
}));
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(
|
||
planner,
|
||
buildSequentialDeps([{ rows: broadRows }, ...monthlyResults])
|
||
);
|
||
|
||
expect(result.derived_value_flow).toMatchObject({
|
||
value_flow_direction: "outgoing_supplier_payout",
|
||
coverage_limited_by_probe_limit: false,
|
||
coverage_recovered_by_period_chunking: true,
|
||
period_chunking_granularity: "month",
|
||
rows_matched: 12,
|
||
rows_with_amount: 12,
|
||
total_amount: 7800,
|
||
first_movement_date: "2020-01-05",
|
||
latest_movement_date: "2020-12-05"
|
||
});
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Requested period coverage was recovered through monthly 1C value-flow probes"
|
||
);
|
||
expect(result.evidence.unknown_facts).not.toContain(
|
||
"Complete requested-period coverage is not proven by the available checked rows"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_monthly_period_chunking_recovered_coverage");
|
||
});
|
||
|
||
it("executes bidirectional value-flow queries and derives guarded net cash flow", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||
{ Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" }
|
||
]
|
||
},
|
||
{
|
||
rows: [{ Period: "2020-03-10T00:00:00", Amount: 4000, Counterparty: "SVK" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_bidirectional_value_flow_query_movements_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["query_movements"]);
|
||
expect(result.derived_value_flow).toBeNull();
|
||
expect(result.derived_bidirectional_value_flow).toMatchObject({
|
||
counterparty: "SVK",
|
||
period_scope: "2020",
|
||
net_amount: 8500.5,
|
||
net_amount_human_ru: "8 500,50 руб.",
|
||
net_direction: "net_incoming",
|
||
coverage_limited_by_probe_limit: false,
|
||
incoming_customer_revenue: {
|
||
rows_matched: 2,
|
||
rows_with_amount: 2,
|
||
total_amount: 12500.5,
|
||
first_movement_date: "2020-01-15",
|
||
latest_movement_date: "2020-02-20"
|
||
},
|
||
outgoing_supplier_payout: {
|
||
rows_matched: 1,
|
||
rows_with_amount: 1,
|
||
total_amount: 4000,
|
||
first_movement_date: "2020-03-10",
|
||
latest_movement_date: "2020-03-10"
|
||
}
|
||
});
|
||
expect(result.evidence.confirmed_facts[0]).toContain("bidirectional value-flow rows");
|
||
expect(result.evidence.inferred_facts[0]).toContain("net value-flow");
|
||
expect(result.evidence.unknown_facts).toContain(
|
||
"Full bidirectional value-flow outside the checked period is not proven by this MCP discovery pilot"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_bidirectional_value_flow_recipes_selected");
|
||
expect(result.reason_codes).toContain("pilot_derived_bidirectional_value_flow_from_confirmed_rows");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
||
});
|
||
|
||
it("preserves explicit date ranges when building bidirectional value-flow probes", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020-01-01..2020-12-31",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }]
|
||
},
|
||
{
|
||
rows: [{ Period: "2020-03-10T00:00:00", Amount: 4000, Counterparty: "SVK" }]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.derived_bidirectional_value_flow?.period_scope).toBe("2020-01-01..2020-12-31");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
||
for (const call of deps.executeAddressMcpQuery.mock.calls) {
|
||
const query = String(call[0]?.query ?? "");
|
||
expect(query).toContain("2020, 1, 1");
|
||
expect(query).toContain("2020, 12, 31");
|
||
}
|
||
});
|
||
|
||
it("derives monthly bidirectional value-flow breakdown when the turn explicitly asks by month", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
asked_aggregation_axis: "month",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
}
|
||
});
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||
{ Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" }
|
||
]
|
||
},
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-10T00:00:00", Amount: 4000, Counterparty: "SVK" },
|
||
{ Period: "2020-02-11T00:00:00", Amount: 1000, Counterparty: "SVK" }
|
||
]
|
||
}
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.derived_bidirectional_value_flow?.aggregation_axis).toBe("month");
|
||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown).toMatchObject([
|
||
{
|
||
month_bucket: "2020-01",
|
||
incoming_total_amount: 10000,
|
||
incoming_rows_with_amount: 1,
|
||
outgoing_total_amount: 4000,
|
||
outgoing_rows_with_amount: 1,
|
||
net_amount: 6000,
|
||
net_direction: "net_incoming"
|
||
},
|
||
{
|
||
month_bucket: "2020-02",
|
||
incoming_total_amount: 2500.5,
|
||
incoming_rows_with_amount: 1,
|
||
outgoing_total_amount: 1000,
|
||
outgoing_rows_with_amount: 1,
|
||
net_amount: 1500.5,
|
||
net_direction: "net_incoming"
|
||
}
|
||
]);
|
||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.incoming_total_amount_human_ru).toContain("10 000");
|
||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.net_amount_human_ru).toContain("6 000");
|
||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.incoming_total_amount_human_ru).toContain("2 500,50");
|
||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.net_amount_human_ru).toContain("1 500,50");
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows");
|
||
});
|
||
|
||
it("recovers bidirectional yearly coverage when one side is rebuilt from monthly subprobes", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||
}
|
||
});
|
||
const outgoingBroadRows = Array.from({ length: 200 }, (_, index) => ({
|
||
Period: `2020-01-${String((index % 28) + 1).padStart(2, "0")}T00:00:00`,
|
||
Amount: 10,
|
||
Counterparty: "SVK"
|
||
}));
|
||
const outgoingMonthlyResults = Array.from({ length: 12 }, (_, index) => ({
|
||
rows: [
|
||
{
|
||
Period: `2020-${String(index + 1).padStart(2, "0")}-10T00:00:00`,
|
||
Amount: (index + 1) * 50,
|
||
Counterparty: "SVK"
|
||
}
|
||
]
|
||
}));
|
||
const deps = buildSequentialDeps([
|
||
{
|
||
rows: [
|
||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||
{ Period: "2020-02-20T00:00:00", Amount: 10000, Counterparty: "SVK" }
|
||
]
|
||
},
|
||
{ rows: outgoingBroadRows },
|
||
...outgoingMonthlyResults
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.derived_bidirectional_value_flow).toMatchObject({
|
||
coverage_limited_by_probe_limit: false,
|
||
coverage_recovered_by_period_chunking: true,
|
||
period_chunking_granularity: "month",
|
||
net_amount: 16100,
|
||
incoming_customer_revenue: {
|
||
total_amount: 20000,
|
||
coverage_limited_by_probe_limit: false
|
||
},
|
||
outgoing_supplier_payout: {
|
||
total_amount: 3900,
|
||
coverage_limited_by_probe_limit: false,
|
||
coverage_recovered_by_period_chunking: true,
|
||
period_chunking_granularity: "month"
|
||
}
|
||
});
|
||
expect(result.evidence.inferred_facts).toContain(
|
||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes"
|
||
);
|
||
expect(result.evidence.unknown_facts).not.toContain(
|
||
"Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows"
|
||
);
|
||
expect(result.reason_codes).toContain("pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||
});
|
||
|
||
it("executes document-ready plans through the dedicated document pilot", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"]
|
||
}
|
||
});
|
||
const deps = buildDeps([]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.executed_primitives).toEqual(["query_documents"]);
|
||
expect(result.reason_codes).toContain("pilot_query_documents_mcp_executed");
|
||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("keeps document and movement evidence scoped to the resolved entity and checked period", async () => {
|
||
const documentPlanner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence"
|
||
}
|
||
});
|
||
const documentResult = await executeAssistantMcpDiscoveryPilot(
|
||
documentPlanner,
|
||
buildDeps([{ Period: "2020-01-15T00:00:00", Counterparty: "Группа СВК", Registrar: "Doc1" }])
|
||
);
|
||
|
||
expect(documentResult.evidence.confirmed_facts).toContain(
|
||
"В 1С найдены строки документов по контрагенту Группа СВК за 2020."
|
||
);
|
||
expect(documentResult.evidence.inferred_facts).toContain(
|
||
"Срез документов по контрагенту Группа СВК за 2020 ограничен только подтвержденными строками документов, найденными этим поиском."
|
||
);
|
||
expect(documentResult.evidence.unknown_facts).toContain(
|
||
"Полный исторический срез документов по контрагенту Группа СВК вне периода 2020 этим поиском не подтвержден."
|
||
);
|
||
|
||
const movementPlanner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence"
|
||
}
|
||
});
|
||
const movementResult = await executeAssistantMcpDiscoveryPilot(
|
||
movementPlanner,
|
||
buildDeps([{ Period: "2020-01-15T00:00:00", Counterparty: "Группа СВК", Registrar: "Move1" }])
|
||
);
|
||
|
||
expect(movementResult.evidence.confirmed_facts).toContain(
|
||
"В 1С найдены строки движений по контрагенту Группа СВК за 2020."
|
||
);
|
||
expect(movementResult.evidence.inferred_facts).toContain(
|
||
"Срез движений по контрагенту Группа СВК за 2020 ограничен только подтвержденными строками движений, найденными этим поиском."
|
||
);
|
||
expect(movementResult.evidence.unknown_facts).toContain(
|
||
"Полный исторический срез движений по контрагенту Группа СВК вне периода 2020 этим поиском не подтвержден."
|
||
);
|
||
});
|
||
|
||
it("records MCP errors as limitations without converting them into facts", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["SVK"]
|
||
}
|
||
});
|
||
const deps = buildDeps([], "MCP fetch failed: timeout");
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.pilot_status).toBe("executed");
|
||
expect(result.mcp_execution_performed).toBe(true);
|
||
expect(result.evidence.evidence_status).toBe("insufficient");
|
||
expect(result.evidence.confirmed_facts).toEqual([]);
|
||
expect(result.derived_activity_period).toBeNull();
|
||
expect(result.query_limitations).toContain("MCP fetch failed: timeout");
|
||
expect(result.reason_codes).toContain("pilot_query_documents_mcp_error");
|
||
});
|
||
|
||
it("emits Russian confirmed and bounded facts for resolved entity grounding", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||
unsupported_but_understood_family: "entity_resolution"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a", CounterpartyRef: "Ref-1" },
|
||
{ Counterparty: "\u0421\u0412\u041a \u041b\u043e\u0433\u0438\u0441\u0442\u0438\u043a\u0430", CounterpartyRef: "Ref-2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("\u043d\u0430\u0439\u0434\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442");
|
||
expect(result.evidence.confirmed_facts.join("\n")).toContain("\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a");
|
||
expect(result.evidence.inferred_facts.join("\n")).toContain(
|
||
"\u041f\u043e\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0443 1\u0421"
|
||
);
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain(
|
||
"\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0438 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438"
|
||
);
|
||
});
|
||
|
||
it("emits Russian ambiguity boundaries for entity grounding", async () => {
|
||
const planner = planAssistantMcpDiscovery({
|
||
turnMeaning: {
|
||
asked_domain_family: "entity_resolution",
|
||
asked_action_family: "search_business_entity",
|
||
explicit_entity_candidates: ["\u0421\u0412\u041a"],
|
||
unsupported_but_understood_family: "entity_resolution"
|
||
}
|
||
});
|
||
const deps = buildDeps([
|
||
{ Counterparty: "\u0421\u0412\u041a-\u0410", CounterpartyRef: "Ref-1" },
|
||
{ Counterparty: "\u0421\u0412\u041a-\u0411", CounterpartyRef: "Ref-2" }
|
||
]);
|
||
|
||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||
|
||
expect(result.evidence.confirmed_facts).toEqual([]);
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain(
|
||
"\u0422\u043e\u0447\u043d\u043e\u0435 \u0437\u0430\u0437\u0435\u043c\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430 \u0432 1\u0421 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u044b\u043c"
|
||
);
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0410");
|
||
expect(result.evidence.unknown_facts.join("\n")).toContain("\u0421\u0412\u041a-\u0411");
|
||
});
|
||
});
|