NODEDC_1C/llm_normalizer/backend/dist/services/assistantMcpDiscoveryPlanne...

786 lines
37 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = void 0;
exports.planAssistantMcpDiscovery = planAssistantMcpDiscovery;
const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
const assistantMcpCatalogIndex_1 = require("./assistantMcpCatalogIndex");
exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1";
function toNonEmptyString(value) {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function lower(value) {
return String(value ?? "").trim().toLowerCase();
}
function normalizeReasonCode(value) {
const normalized = value
.trim()
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
.replace(/^_+|_+$/g, "")
.toLowerCase();
return normalized.length > 0 ? normalized.slice(0, 120) : null;
}
function pushReason(target, value) {
const normalized = normalizeReasonCode(value);
if (normalized && !target.includes(normalized)) {
target.push(normalized);
}
}
function pushUnique(target, value) {
const text = value.trim();
if (text && !target.includes(text)) {
target.push(text);
}
}
function hasEntity(meaning) {
return (meaning?.explicit_entity_candidates?.length ?? 0) > 0;
}
function hasSubjectCandidates(graph) {
return (graph?.subject_candidates.length ?? 0) > 0;
}
function hasMetadataScopedOpenLane(graph, meaning) {
return Boolean(graph?.subject_resolution_optional === true ||
meaning?.subject_resolution_optional === true);
}
function hasReasonCode(graph, reasonCode) {
return (graph?.reason_codes ?? []).includes(reasonCode);
}
function aggregationAxis(meaning) {
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
}
function addScopeAxes(axes, meaning) {
if (hasEntity(meaning)) {
pushUnique(axes, "counterparty");
}
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
pushUnique(axes, "organization");
}
if (toNonEmptyString(meaning?.explicit_date_scope)) {
pushUnique(axes, "period");
}
}
function addMetadataScopeAxis(axes, meaning) {
if (toNonEmptyString(meaning?.metadata_scope_hint)) {
pushUnique(axes, "metadata_scope");
}
}
function addTimeScopeAxes(axes, dataNeedGraph) {
if (dataNeedGraph?.time_scope_need === "all_time_scope") {
pushUnique(axes, "all_time_scope");
}
}
function includesAny(text, tokens) {
return tokens.some((token) => text.includes(token));
}
function isYearDateScope(meaning) {
return /^\d{4}$/.test(toNonEmptyString(meaning?.explicit_date_scope) ?? "");
}
function mergeCatalogPrimitivesWithFallback(catalogPrimitives, fallbackPrimitives) {
const result = [];
for (const primitive of fallbackPrimitives) {
if (catalogPrimitives.includes(primitive) && !result.includes(primitive)) {
result.push(primitive);
}
}
for (const primitive of catalogPrimitives) {
if (!result.includes(primitive)) {
result.push(primitive);
}
}
for (const primitive of fallbackPrimitives) {
if (!result.includes(primitive)) {
result.push(primitive);
}
}
return result;
}
function preferredPrimitiveFromMetadataSurface(surface) {
const recommendedPrimitive = surface?.recommended_next_primitive ?? null;
if (recommendedPrimitive) {
return recommendedPrimitive;
}
if (surface?.ambiguity_detected) {
return null;
}
if (surface?.downstream_route_family === "document_evidence") {
return "query_documents";
}
if (surface?.downstream_route_family === "movement_evidence") {
return "query_movements";
}
if (surface?.downstream_route_family === "catalog_drilldown") {
return "drilldown_related_objects";
}
return null;
}
function filterCatalogPrimitivesByMetadataSurface(input) {
const preferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
const reasonCodes = [];
if (!preferredPrimitive ||
input.metadataSurface?.ambiguity_detected ||
!input.fallbackPrimitives.includes(preferredPrimitive)) {
return {
primitives: input.catalogPrimitives,
reasonCodes
};
}
const laneSensitivePrimitives = new Set([
"query_documents",
"query_movements",
"drilldown_related_objects"
]);
const filteredPrimitives = input.catalogPrimitives.filter((primitive) => !laneSensitivePrimitives.has(primitive) || primitive === preferredPrimitive);
if (filteredPrimitives.length !== input.catalogPrimitives.length) {
reasonCodes.push("planner_filtered_catalog_primitives_by_confirmed_metadata_surface");
}
if ((filteredPrimitives.includes(preferredPrimitive) || input.fallbackPrimitives.includes(preferredPrimitive)) &&
input.metadataSurface?.selected_surface_objects.length) {
reasonCodes.push("planner_surface_aware_next_lane_from_confirmed_metadata_objects");
}
return {
primitives: filteredPrimitives,
reasonCodes
};
}
function selectPrimitivesFromGraphAndCatalog(input) {
const reasonCodes = [];
const decompositionCandidates = input.dataNeedGraph?.decomposition_candidates ?? [];
const decompositionPrimitives = decompositionCandidates.length > 0
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogPrimitivesByDecompositionCandidates)({
decomposition_candidates: decompositionCandidates,
allow_aggregate_by_axis: input.allowAggregateByAxis
})
: [];
if (decompositionPrimitives.length > 0) {
reasonCodes.push("planner_selected_catalog_primitives_from_decomposition_candidates");
}
const metadataSurfacePrimitives = input.metadataSurface
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogPrimitivesByMetadataSurface)({
downstream_route_family: input.metadataSurface.downstream_route_family,
selected_entity_set: input.metadataSurface.selected_entity_set,
selected_surface_objects: input.metadataSurface.selected_surface_objects,
recommended_next_primitive: input.metadataSurface.recommended_next_primitive,
required_axes: input.requiredAxes,
allow_aggregate_by_axis: input.allowAggregateByAxis
})
: [];
if (metadataSurfacePrimitives.length > 0) {
reasonCodes.push("planner_selected_catalog_primitives_from_metadata_surface_search");
}
const factAxisPrimitives = input.dataNeedGraph
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogPrimitivesByFactAxis)({
business_fact_family: input.dataNeedGraph.business_fact_family,
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
required_axes: input.requiredAxes,
comparison_need: input.dataNeedGraph.comparison_need,
ranking_need: input.dataNeedGraph.ranking_need,
aggregation_need: input.dataNeedGraph.aggregation_need,
has_subject_candidates: hasSubjectCandidates(input.dataNeedGraph),
allow_aggregate_by_axis: input.allowAggregateByAxis
})
: [];
if (factAxisPrimitives.length > 0) {
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
}
const combinedCatalogPrimitives = [];
for (const primitive of decompositionPrimitives) {
if (!combinedCatalogPrimitives.includes(primitive)) {
combinedCatalogPrimitives.push(primitive);
}
}
for (const primitive of metadataSurfacePrimitives) {
if (!combinedCatalogPrimitives.includes(primitive)) {
combinedCatalogPrimitives.push(primitive);
}
}
for (const primitive of factAxisPrimitives) {
if (!combinedCatalogPrimitives.includes(primitive)) {
combinedCatalogPrimitives.push(primitive);
}
}
const filteredCatalogPrimitives = filterCatalogPrimitivesByMetadataSurface({
catalogPrimitives: combinedCatalogPrimitives,
fallbackPrimitives: input.fallbackPrimitives,
metadataSurface: input.metadataSurface
});
reasonCodes.push(...filteredCatalogPrimitives.reasonCodes);
if (filteredCatalogPrimitives.primitives.length <= 0) {
return {
primitives: input.fallbackPrimitives,
reasonCodes: ["planner_fell_back_to_recipe_primitives_after_empty_catalog_search"]
};
}
const mergedPrimitives = mergeCatalogPrimitivesWithFallback(filteredCatalogPrimitives.primitives, input.fallbackPrimitives);
if (input.fallbackPrimitives.some((primitive) => !filteredCatalogPrimitives.primitives.includes(primitive))) {
reasonCodes.push("planner_completed_catalog_searched_chain_with_recipe_primitives");
}
return {
primitives: mergedPrimitives,
reasonCodes
};
}
function budgetOverrideFor(input, recipe) {
const meaning = input.turnMeaning ?? null;
const requestedAggregationAxis = aggregationAxis(meaning);
const isValueFlowRecipe = recipe.semanticDataNeed === "counterparty value-flow evidence" &&
recipe.primitives.includes("query_movements");
if (!isValueFlowRecipe) {
return {};
}
if (requestedAggregationAxis === "month" || isYearDateScope(meaning)) {
return {
maxProbeCount: 30
};
}
return {};
}
function routeFamilyFromThinMetadataSurfaceInput(input) {
const surface = input.metadataSurface ?? null;
if (!surface || surface.ambiguity_detected || !surface.downstream_route_family || !surface.recommended_next_primitive) {
return null;
}
const meaning = input.turnMeaning ?? null;
const dataNeedGraph = input.dataNeedGraph ?? null;
const graphFactFamily = lower(dataNeedGraph?.business_fact_family);
const domain = lower(meaning?.asked_domain_family);
const action = lower(meaning?.asked_action_family);
const unsupported = lower(meaning?.unsupported_but_understood_family);
const semanticNeed = lower(input.semanticDataNeed);
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`.trim();
const explicitlyOtherFamily = includesAny(combined, ["value_flow", "turnover", "revenue", "payment", "payout", "net", "lifecycle", "activity", "duration", "metadata lane clarification"]);
if (explicitlyOtherFamily) {
return null;
}
if (graphFactFamily === "document_evidence" || includesAny(combined, ["document", "documents", "list_documents"])) {
return surface.downstream_route_family === "document_evidence" ? "document_evidence" : null;
}
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
return surface.downstream_route_family === "movement_evidence" ? "movement_evidence" : null;
}
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
return surface.downstream_route_family === "catalog_drilldown" ? "catalog_drilldown" : null;
}
if (!graphFactFamily && !domain && !action) {
if (surface.downstream_route_family === "document_evidence" ||
surface.downstream_route_family === "movement_evidence" ||
surface.downstream_route_family === "catalog_drilldown") {
return surface.downstream_route_family;
}
}
return null;
}
function recipeFor(input) {
const meaning = input.turnMeaning ?? null;
const dataNeedGraph = input.dataNeedGraph ?? null;
const domain = lower(meaning?.asked_domain_family);
const action = lower(meaning?.asked_action_family);
const unsupported = lower(meaning?.unsupported_but_understood_family);
const graphFactFamily = lower(dataNeedGraph?.business_fact_family);
const graphAction = lower(dataNeedGraph?.action_family);
const graphAggregation = lower(dataNeedGraph?.aggregation_need);
const graphClarificationGaps = (dataNeedGraph?.clarification_gaps ?? []).map((item) => lower(item));
const metadataScopedOpenLane = hasMetadataScopedOpenLane(dataNeedGraph, meaning);
const openScopeTotalWithoutSubject = graphFactFamily === "value_flow" &&
!hasSubjectCandidates(dataNeedGraph) &&
hasReasonCode(dataNeedGraph, "data_need_graph_open_scope_total_without_subject");
const combined = `${domain} ${action} ${unsupported}`.trim();
const axes = [];
const requestedAggregationAxis = aggregationAxis(meaning);
addScopeAxes(axes, meaning);
addMetadataScopeAxis(axes, meaning);
addTimeScopeAxes(axes, dataNeedGraph);
if (graphClarificationGaps.includes("lane_family_choice")) {
pushUnique(axes, "lane_family_choice");
return {
semanticDataNeed: "metadata lane clarification",
chainId: "metadata_lane_clarification",
chainSummary: "Preserve the ambiguous metadata surface and ask the user to choose the next data lane before running MCP probes.",
primitives: [],
axes,
reason: "planner_selected_metadata_lane_clarification_from_data_need_graph"
};
}
const thinSurfaceRouteFamily = routeFamilyFromThinMetadataSurfaceInput(input);
if (thinSurfaceRouteFamily === "document_evidence") {
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "document evidence",
chainId: "document_evidence",
chainSummary: "Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
extraReasons: primitiveSelection.reasonCodes
};
}
if (thinSurfaceRouteFamily === "movement_evidence") {
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "movement evidence",
chainId: "movement_evidence",
chainSummary: "Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
extraReasons: primitiveSelection.reasonCodes
};
}
if (thinSurfaceRouteFamily === "catalog_drilldown") {
pushUnique(axes, "metadata_scope");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["inspect_1c_metadata"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "catalog drilldown metadata evidence",
chainId: "catalog_drilldown",
chainSummary: "Drill deeper into the confirmed catalog-oriented metadata surface, inspect related metadata objects, and keep the next safe lane grounded in checked schema evidence.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "value_flow") {
if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing" && !hasSubjectCandidates(dataNeedGraph)) {
pushUnique(axes, "amount");
pushUnique(axes, "coverage_target");
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
pushUnique(axes, "calendar_month");
}
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action,
allowAggregateByAxis: false
});
return {
semanticDataNeed: "bidirectional value-flow comparison evidence",
chainId: "value_flow_comparison",
chainSummary: "Query incoming and outgoing movements for the checked period and organization, compare the checked sides, and probe coverage before answering a bounded comparison.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_bidirectional_value_flow_comparison_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (dataNeedGraph?.ranking_need && !hasSubjectCandidates(dataNeedGraph)) {
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action,
allowAggregateByAxis: true
});
return {
semanticDataNeed: "ranked value-flow evidence",
chainId: "value_flow_ranking",
chainSummary: "Query scoped movements for the checked period and organization, aggregate checked amounts by counterparty, then probe coverage before answering a bounded ranking.",
primitives: primitiveSelection.primitives,
axes,
reason: dataNeedGraph.ranking_need === "bottom_asc"
? "planner_selected_bottom_ranked_value_flow_from_data_need_graph"
: "planner_selected_top_ranked_value_flow_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (openScopeTotalWithoutSubject) {
pushUnique(axes, "organization");
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
pushUnique(axes, "coverage_target");
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
pushUnique(axes, "calendar_month");
}
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
requiredAxes: axes,
actionFamily: action,
allowAggregateByAxis: true
});
return {
semanticDataNeed: "organization-scoped value-flow evidence",
chainId: "value_flow",
chainSummary: "Query scoped movements for the checked period and organization without a preselected counterparty, aggregate checked amounts, then probe coverage before answering a bounded total.",
primitives: primitiveSelection.primitives,
axes,
reason: requestedAggregationAxis === "month" || graphAggregation === "by_month"
? "planner_selected_monthly_open_scope_value_flow_total_from_data_need_graph"
: "planner_selected_open_scope_value_flow_total_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
pushUnique(axes, "coverage_target");
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
pushUnique(axes, "calendar_month");
}
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action,
allowAggregateByAxis: true
});
return {
semanticDataNeed: "counterparty value-flow evidence",
chainId: "value_flow",
chainSummary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
primitives: primitiveSelection.primitives,
axes,
reason: requestedAggregationAxis === "month" || graphAggregation === "by_month"
? "planner_selected_monthly_value_flow_from_data_need_graph"
: "planner_selected_value_flow_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "activity_lifecycle") {
pushUnique(axes, "document_date");
pushUnique(axes, "coverage_target");
pushUnique(axes, "evidence_basis");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "counterparty lifecycle evidence",
chainId: "lifecycle",
chainSummary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_lifecycle_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "schema_surface") {
pushUnique(axes, "metadata_scope");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["inspect_1c_metadata"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "1C metadata evidence",
chainId: "metadata_inspection",
chainSummary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_metadata_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "movement_evidence") {
if (metadataScopedOpenLane) {
pushUnique(axes, "organization");
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "movement evidence",
chainId: "movement_evidence",
chainSummary: "Keep the metadata-scoped movement lane, ask only for the remaining business scope, then fetch scoped movement rows and probe coverage without pretending there is a grounded counterparty.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_metadata_scoped_movement_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "movement evidence",
chainId: "movement_evidence",
chainSummary: "Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_movement_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "document_evidence") {
if (metadataScopedOpenLane) {
pushUnique(axes, "organization");
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["query_documents", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "document evidence",
chainId: "document_evidence",
chainSummary: "Keep the metadata-scoped document lane, ask only for the remaining business scope, then fetch scoped document rows and probe coverage without pretending there is a grounded counterparty.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_metadata_scoped_document_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "document evidence",
chainId: "document_evidence",
chainSummary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
primitives: primitiveSelection.primitives,
axes,
reason: "planner_selected_document_from_data_need_graph",
extraReasons: primitiveSelection.reasonCodes
};
}
if (graphFactFamily === "entity_grounding" || (!graphFactFamily && (dataNeedGraph?.subject_candidates.length ?? 0) > 0)) {
pushUnique(axes, "business_entity");
pushUnique(axes, "coverage_target");
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
dataNeedGraph,
fallbackPrimitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
requiredAxes: axes,
metadataSurface: input.metadataSurface,
actionFamily: action
});
return {
semanticDataNeed: "entity discovery evidence",
chainId: "entity_resolution",
chainSummary: "Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
primitives: primitiveSelection.primitives,
axes,
reason: graphAction === "search_business_entity"
? "planner_selected_entity_resolution_from_data_need_graph"
: "planner_selected_entity_resolution_recipe",
extraReasons: primitiveSelection.reasonCodes
};
}
if (includesAny(combined, ["metadata_lane_choice_clarification", "resolve_next_lane"])) {
pushUnique(axes, "lane_family_choice");
return {
semanticDataNeed: "metadata lane clarification",
chainId: "metadata_lane_clarification",
chainSummary: "Preserve the ambiguous metadata surface and ask the user to choose the next data lane before running MCP probes.",
primitives: [],
axes,
reason: "planner_selected_metadata_lane_clarification_recipe"
};
}
if (includesAny(combined, ["turnover", "revenue", "payment", "payout", "value", "net", "netting", "balance", "cashflow"])) {
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
pushUnique(axes, "coverage_target");
if (requestedAggregationAxis === "month") {
pushUnique(axes, "calendar_month");
}
return {
semanticDataNeed: "counterparty value-flow evidence",
chainId: "value_flow",
chainSummary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
primitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
axes,
reason: requestedAggregationAxis === "month"
? "planner_selected_monthly_value_flow_recipe"
: "planner_selected_value_flow_recipe"
};
}
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
pushUnique(axes, "document_date");
pushUnique(axes, "coverage_target");
pushUnique(axes, "evidence_basis");
return {
semanticDataNeed: "counterparty lifecycle evidence",
chainId: "lifecycle",
chainSummary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
axes,
reason: "planner_selected_lifecycle_recipe"
};
}
if (includesAny(combined, ["metadata", "schema", "catalog"])) {
pushUnique(axes, "metadata_scope");
return {
semanticDataNeed: "1C metadata evidence",
chainId: "metadata_inspection",
chainSummary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
primitives: ["inspect_1c_metadata"],
axes,
reason: "planner_selected_metadata_recipe"
};
}
if (includesAny(combined, ["movement", "movements", "bank_operations", "movement_evidence", "list_movements"])) {
pushUnique(axes, "coverage_target");
return {
semanticDataNeed: "movement evidence",
chainId: "movement_evidence",
chainSummary: "Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
axes,
reason: "planner_selected_movement_recipe"
};
}
if (includesAny(combined, ["document", "documents"])) {
pushUnique(axes, "coverage_target");
return {
semanticDataNeed: "document evidence",
chainId: "document_evidence",
chainSummary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
axes,
reason: "planner_selected_document_recipe"
};
}
if (hasEntity(meaning)) {
pushUnique(axes, "business_entity");
pushUnique(axes, "coverage_target");
return {
semanticDataNeed: "entity discovery evidence",
chainId: "entity_resolution",
chainSummary: "Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
primitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
axes,
reason: "planner_selected_entity_resolution_recipe"
};
}
return {
semanticDataNeed: "unclassified 1C discovery need",
chainId: "metadata_inspection",
chainSummary: "Start with metadata inspection instead of guessing a deeper fact route when the business need is still under-specified.",
primitives: ["inspect_1c_metadata"],
axes,
reason: "planner_selected_clarification_recipe"
};
}
function statusFrom(plan, review) {
if (plan.plan_status === "blocked" || review.review_status === "catalog_blocked") {
return "blocked";
}
if (plan.plan_status !== "allowed" || review.review_status !== "catalog_compatible") {
return "needs_clarification";
}
return "ready_for_execution";
}
function planAssistantMcpDiscovery(input) {
const recipe = recipeFor(input);
const budgetOverride = budgetOverrideFor(input, recipe);
const semanticDataNeed = toNonEmptyString(input.semanticDataNeed) ?? recipe.semanticDataNeed;
const dataNeedGraph = input.dataNeedGraph ?? null;
const metadataSurface = input.metadataSurface ?? null;
const reasonCodes = [];
pushReason(reasonCodes, recipe.reason);
for (const reason of recipe.extraReasons ?? []) {
pushReason(reasonCodes, reason);
}
if (dataNeedGraph) {
pushReason(reasonCodes, "planner_consumed_data_need_graph_v1");
}
if (metadataSurface) {
pushReason(reasonCodes, "planner_consumed_metadata_surface_ref_v1");
}
if (budgetOverride.maxProbeCount) {
pushReason(reasonCodes, "planner_enabled_chunked_coverage_probe_budget");
}
const plan = (0, assistantMcpDiscoveryPolicy_1.buildAssistantMcpDiscoveryPlan)({
semanticDataNeed,
turnMeaning: input.turnMeaning,
proposedPrimitives: recipe.primitives,
requiredAxes: recipe.axes,
clarificationGaps: dataNeedGraph?.clarification_gaps ?? [],
maxProbeCount: budgetOverride.maxProbeCount
});
const review = (0, assistantMcpCatalogIndex_1.reviewAssistantMcpDiscoveryPlanAgainstCatalog)(plan);
const organizationClarificationRequired = (dataNeedGraph?.clarification_gaps ?? []).includes("organization") &&
!toNonEmptyString(input.turnMeaning?.explicit_organization_scope);
const adjustedReview = organizationClarificationRequired && recipe.primitives.includes("query_movements")
? {
...review,
review_status: "needs_more_axes",
missing_axes_by_primitive: {
...review.missing_axes_by_primitive,
query_movements: review.missing_axes_by_primitive.query_movements?.length
? review.missing_axes_by_primitive.query_movements
: [["organization"]]
},
reason_codes: review.reason_codes.includes("catalog_requires_organization_scope_from_data_need_graph")
? review.reason_codes
: [...review.reason_codes, "catalog_requires_organization_scope_from_data_need_graph"]
}
: review;
const plannerStatus = organizationClarificationRequired ? "needs_clarification" : statusFrom(plan, adjustedReview);
if (organizationClarificationRequired) {
pushReason(reasonCodes, "planner_requires_organization_scope_from_data_need_graph");
}
if (plannerStatus === "ready_for_execution") {
pushReason(reasonCodes, "planner_ready_for_guarded_mcp_execution");
}
else if (plannerStatus === "blocked") {
pushReason(reasonCodes, "planner_blocked_by_policy_or_catalog");
}
else {
pushReason(reasonCodes, "planner_needs_more_user_or_scope_context");
}
return {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryPlanner",
planner_status: plannerStatus,
semantic_data_need: semanticDataNeed,
data_need_graph: dataNeedGraph,
metadata_surface_ref: metadataSurface,
selected_chain_id: recipe.chainId,
selected_chain_summary: recipe.chainSummary,
proposed_primitives: recipe.primitives,
required_axes: recipe.axes,
discovery_plan: plan,
catalog_review: adjustedReview,
reason_codes: reasonCodes
};
}