666 lines
29 KiB
TypeScript
666 lines
29 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { buildAssistantMcpDiscoveryTurnInput } from "../src/services/assistantMcpDiscoveryTurnInputAdapter";
|
||
|
||
describe("assistant MCP discovery turn input adapter", () => {
|
||
it("maps unsupported assistant turn meaning into a discovery-ready value-flow input", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
schema_version: "assistant_turn_meaning_v1",
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_value_or_turnover",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true,
|
||
explicit_entity_candidates: [{ type: "counterparty", value: "SVK", source: "current_turn_loose_entity_tail" }]
|
||
},
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК", organization: "Альтернатива" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK", "Группа СВК"]);
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBe("Альтернатива");
|
||
expect(result.turn_meaning_ref?.explicit_date_scope).toBe("2020");
|
||
expect(result.turn_meaning_ref?.stale_replay_forbidden).toBe(true);
|
||
});
|
||
|
||
it("bootstraps lifecycle discovery from raw user wording and predecompose scope", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "Сколько лет мы работаем с Группа СВК?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: null, period_to: null, as_of_date: null }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.source_signal).toBe("predecompose_contract");
|
||
expect(result.semantic_data_need).toBe("counterparty lifecycle evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_lifecycle",
|
||
asked_action_family: "activity_duration",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
unsupported_but_understood_family: "counterparty_lifecycle",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_lifecycle_signal_detected");
|
||
});
|
||
|
||
it("bootstraps value-flow discovery from raw turnover wording when no exact route owns it", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какой денежный поток был у Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "turnover",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("treats value-flow organization-shaped target as entity candidate when counterparty is absent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какой денежный поток был у Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { organization: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["Группа СВК"]);
|
||
expect(result.turn_meaning_ref?.explicit_organization_scope).toBeUndefined();
|
||
});
|
||
|
||
it("keeps payout wording as outgoing supplier-payout discovery", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "сколько мы заплатили Группа СВК за 2020 год?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_payout_signal_detected");
|
||
});
|
||
|
||
it("keeps net cash wording as bidirectional value-flow discovery", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какое нетто по деньгам с Группа СВК за 2020: сколько получили и сколько заплатили?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_bidirectional_value_flow_signal_detected");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_payout_signal_detected");
|
||
});
|
||
|
||
it("captures monthly aggregation as part of bidirectional value-flow meaning", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год по месяцам: сколько получили и сколько заплатили помесячно?",
|
||
predecomposeContract: {
|
||
entities: { counterparty: "Группа СВК" },
|
||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
asked_aggregation_axis: "month",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_date_scope: "2020"
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
||
});
|
||
|
||
it("bootstraps metadata discovery from raw schema wording", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "какие регистры и поля есть в 1С по НДС?"
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("raw_text");
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_fields",
|
||
unsupported_but_understood_family: "1c_metadata_surface",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||
});
|
||
|
||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по месяцам?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "net_value_flow",
|
||
asked_aggregation_axis: "month",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_seeded_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_date_scope_from_followup_context");
|
||
});
|
||
|
||
it("seeds short metadata follow-up from prior metadata discovery context", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а по регистрам?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_filters: {
|
||
counterparty: "НДС"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "НДС"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "inspect_registers",
|
||
explicit_entity_candidates: ["НДС"],
|
||
unsupported_but_understood_family: "1c_metadata_surface",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_seeded_from_followup_context");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("pivots grounded metadata follow-up into document evidence when the next lane is explicit", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "then documents",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "document_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "Документ",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_document_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("pivots grounded metadata follow-up into movement evidence when the next lane is explicit", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "then movements",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "movement_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_movement_followup");
|
||
expect(result.reason_codes).toContain("mcp_discovery_counterparty_from_followup_context");
|
||
});
|
||
|
||
it("continues from grounded metadata into document evidence on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "давай дальше",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "document_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "Документ",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||
});
|
||
|
||
it("continues from grounded metadata into movement evidence on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_route_family: "movement_evidence",
|
||
previous_discovery_metadata_selected_entity_set: "РегистрНакопления",
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into document lane when the follow-up explicitly asks for documents", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по документам",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_document_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into movement lane when the follow-up explicitly asks for movements", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into document lane from semantic document hints", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "покажи счет-фактуры",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_document_lane");
|
||
});
|
||
|
||
it("resolves ambiguous metadata surface into movement lane from semantic movement hints without turning it into value-flow", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "покажи платежи",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_value_flow_signal_detected");
|
||
});
|
||
|
||
it("does not resolve metadata ambiguity into movement lane when confirmed ambiguity sets contain documents only", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "по движениям",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.reason_codes).not.toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||
});
|
||
|
||
it("continues from ambiguous metadata into document lane when ambiguity choice set collapses to documents on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "invoice"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("document evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "documents",
|
||
asked_action_family: "list_documents",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "document_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_document_lane");
|
||
});
|
||
|
||
it("continues from ambiguous metadata into movement lane when ambiguity choice set collapses to movements on a generic downstream follow-up", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "continue with data",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["РегистрНакопления", "movement"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.semantic_data_need).toBe("movement evidence");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "movements",
|
||
asked_action_family: "list_movements",
|
||
explicit_entity_candidates: ["SVK"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "movement_evidence",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_collapsed_to_movement_lane");
|
||
});
|
||
|
||
it("requires an explicit lane choice on a generic downstream follow-up when metadata ambiguity stays mixed", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "давай дальше",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "metadata_inspection_v1",
|
||
previous_discovery_metadata_ambiguity_detected: true,
|
||
previous_discovery_metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
previous_filters: {
|
||
counterparty: "SVK",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "SVK"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.source_signal).toBe("followup_context");
|
||
expect(result.semantic_data_need).toBe("metadata lane clarification");
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "metadata",
|
||
asked_action_family: "resolve_next_lane",
|
||
explicit_entity_candidates: ["SVK"],
|
||
metadata_ambiguity_entity_sets: ["Документ", "РегистрНакопления"],
|
||
explicit_date_scope: "2020",
|
||
unsupported_but_understood_family: "metadata_lane_choice_clarification",
|
||
stale_replay_forbidden: true
|
||
});
|
||
expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_requires_lane_choice");
|
||
});
|
||
|
||
it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
userMessage: "а теперь за 2021?",
|
||
followupContext: {
|
||
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||
previous_filters: {
|
||
counterparty: "Группа СВК",
|
||
organization: "ООО Альтернатива Плюс",
|
||
period_from: "2020-01-01",
|
||
period_to: "2020-12-31"
|
||
},
|
||
previous_anchor_type: "counterparty",
|
||
previous_anchor_value: "Группа СВК"
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("ready");
|
||
expect(result.should_run_discovery).toBe(true);
|
||
expect(result.turn_meaning_ref).toMatchObject({
|
||
asked_domain_family: "counterparty_value",
|
||
asked_action_family: "payout",
|
||
explicit_entity_candidates: ["Группа СВК"],
|
||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||
explicit_date_scope: "2021",
|
||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||
stale_replay_forbidden: true
|
||
});
|
||
});
|
||
|
||
it("does not activate discovery for supported exact current-turn intent", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "list_documents",
|
||
explicit_intent_candidate: "list_documents_by_counterparty",
|
||
explicit_entity_candidates: [{ value: "SVK" }],
|
||
stale_replay_forbidden: false
|
||
}
|
||
});
|
||
|
||
expect(result.adapter_status).toBe("not_applicable");
|
||
expect(result.should_run_discovery).toBe(false);
|
||
expect(result.turn_meaning_ref).toBeNull();
|
||
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
});
|
||
|
||
it("never serializes object candidates as [object Object]", () => {
|
||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||
assistantTurnMeaning: {
|
||
asked_domain_family: "counterparty",
|
||
asked_action_family: "counterparty_value_or_turnover",
|
||
unsupported_but_understood_family: "counterparty_value_or_turnover",
|
||
explicit_entity_candidates: [{ type: "counterparty", value: "SVK" }]
|
||
}
|
||
});
|
||
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK"]);
|
||
expect(result.turn_meaning_ref?.explicit_entity_candidates).not.toContain("[object Object]");
|
||
});
|
||
});
|