ARCH: довести surface-aware planner и catalog search

This commit is contained in:
2026-04-23 10:25:03 +03:00
parent 8b6cd4c329
commit 3a89cca6bb
30 changed files with 2521 additions and 157 deletions
@@ -270,6 +270,13 @@ export function readAssistantMcpDiscoveryMetadataRouteFamily(
return toNonEmptyString(readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.downstream_route_family);
}
export function readAssistantMcpDiscoveryMetadataRouteFamilySelectionBasis(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
): string | null {
return toNonEmptyString(readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.route_family_selection_basis);
}
export function readAssistantMcpDiscoveryMetadataSelectedEntitySet(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
@@ -277,6 +284,24 @@ export function readAssistantMcpDiscoveryMetadataSelectedEntitySet(
return toNonEmptyString(readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.selected_entity_set);
}
export function readAssistantMcpDiscoveryMetadataSelectedSurfaceObjects(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
): string[] {
const values = readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.selected_surface_objects;
if (!Array.isArray(values)) {
return [];
}
return values.map((item) => toNonEmptyString(item)).filter((item): item is string => Boolean(item));
}
export function readAssistantMcpDiscoveryMetadataRecommendedNextPrimitive(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
): string | null {
return toNonEmptyString(readAssistantMcpDiscoveryDerivedMetadataSurface(debug)?.recommended_next_primitive);
}
export function readAssistantMcpDiscoveryMetadataAmbiguityDetected(
debug: Record<string, unknown> | null
): boolean {
@@ -14,6 +14,9 @@ export interface AssistantMcpCatalogPrimitiveContract {
primitive_id: AssistantMcpDiscoveryPrimitive;
purpose: string;
decomposition_hints: string[];
supported_fact_families: string[];
supported_action_families: string[];
planning_tags: string[];
required_axes_any_of: string[][];
optional_axes: string[];
output_fact_kinds: string[];
@@ -45,6 +48,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "search_business_entity",
purpose: "Find candidate 1C business entities by user wording before a fact query is executed.",
decomposition_hints: ["search_business_entity"],
supported_fact_families: ["entity_grounding"],
supported_action_families: ["search_business_entity"],
planning_tags: ["subject_resolution"],
required_axes_any_of: [["business_entity"], ["counterparty"], ["organization"], ["contract"], ["item"]],
optional_axes: ["period", "document", "account"],
output_fact_kinds: ["entity_candidates", "entity_ambiguity"],
@@ -56,6 +62,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "inspect_1c_metadata",
purpose: "Inspect available 1C schema/catalog/document/register surface before selecting a query lane.",
decomposition_hints: ["inspect_metadata_surface"],
supported_fact_families: ["schema_surface"],
supported_action_families: ["inspect_catalog", "inspect_documents", "inspect_registers", "inspect_fields", "inspect_surface"],
planning_tags: ["metadata", "surface_inspection"],
required_axes_any_of: [["metadata_scope"], ["domain_family"], ["document"], ["register"]],
optional_axes: ["business_entity", "account", "counterparty"],
output_fact_kinds: ["available_fields", "available_entity_sets", "known_limitations"],
@@ -67,6 +76,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "resolve_entity_reference",
purpose: "Resolve a user-visible entity name to a concrete 1C reference candidate.",
decomposition_hints: ["resolve_entity_reference"],
supported_fact_families: ["entity_grounding", "value_flow", "document_evidence", "movement_evidence", "activity_lifecycle"],
supported_action_families: ["search_business_entity", "turnover", "payout", "net_value_flow", "list_documents", "list_movements", "activity_duration"],
planning_tags: ["subject_resolution"],
required_axes_any_of: [["business_entity"], ["counterparty"], ["organization"], ["contract"], ["item"]],
optional_axes: ["period", "inn", "document"],
output_fact_kinds: ["resolved_entity_ref", "entity_conflict"],
@@ -83,6 +95,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
"collect_outgoing_movements",
"fetch_scoped_movements"
],
supported_fact_families: ["value_flow", "movement_evidence"],
supported_action_families: ["turnover", "payout", "net_value_flow", "list_movements"],
planning_tags: ["movement", "comparison", "ranking", "aggregation", "monthly_aggregation"],
required_axes_any_of: [["period", "account"], ["period", "counterparty"], ["period", "organization"]],
optional_axes: ["contract", "document", "amount", "item", "warehouse"],
output_fact_kinds: ["movement_rows", "turnover", "balance_delta"],
@@ -94,6 +109,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "query_documents",
purpose: "Fetch documents related to a scoped entity, period, contract, or movement explanation.",
decomposition_hints: ["fetch_scoped_documents", "fetch_supporting_documents"],
supported_fact_families: ["document_evidence", "activity_lifecycle"],
supported_action_families: ["list_documents", "activity_duration"],
planning_tags: ["document"],
required_axes_any_of: [["document"], ["counterparty"], ["contract"], ["period", "organization"]],
optional_axes: ["account", "amount", "item", "warehouse"],
output_fact_kinds: ["document_rows", "document_dates", "document_amounts"],
@@ -105,6 +123,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "aggregate_by_axis",
purpose: "Aggregate already-scoped 1C evidence by a business axis such as counterparty, contract, or period.",
decomposition_hints: ["aggregate_checked_amounts", "aggregate_ranked_axis_values", "aggregate_by_month"],
supported_fact_families: ["value_flow"],
supported_action_families: ["turnover", "payout", "net_value_flow"],
planning_tags: ["aggregation", "ranking", "monthly_aggregation"],
required_axes_any_of: [["aggregate_axis", "period"], ["aggregate_axis", "counterparty"], ["aggregate_axis", "account"]],
optional_axes: ["organization", "contract", "document", "amount"],
output_fact_kinds: ["aggregate_totals", "ranked_axis_values"],
@@ -116,6 +137,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "drilldown_related_objects",
purpose: "Drill from a known entity or document into related contracts, documents, movements, or payments.",
decomposition_hints: ["drilldown_related_objects"],
supported_fact_families: [],
supported_action_families: [],
planning_tags: ["drilldown"],
required_axes_any_of: [["business_entity"], ["document"], ["contract"], ["counterparty"]],
optional_axes: ["period", "account", "amount"],
output_fact_kinds: ["related_objects", "relationship_edges"],
@@ -127,6 +151,29 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "probe_coverage",
purpose: "Check whether the selected MCP/schema route can prove the requested fact or only support a bounded inference.",
decomposition_hints: ["probe_coverage"],
supported_fact_families: [
"schema_surface",
"entity_grounding",
"value_flow",
"document_evidence",
"movement_evidence",
"activity_lifecycle"
],
supported_action_families: [
"inspect_catalog",
"inspect_documents",
"inspect_registers",
"inspect_fields",
"inspect_surface",
"search_business_entity",
"turnover",
"payout",
"net_value_flow",
"list_documents",
"list_movements",
"activity_duration"
],
planning_tags: ["coverage"],
required_axes_any_of: [["coverage_target"], ["domain_family"], ["primitive_id"]],
optional_axes: ["period", "organization", "counterparty", "document", "account"],
output_fact_kinds: ["coverage_status", "known_gaps"],
@@ -138,6 +185,9 @@ const PRIMITIVE_CONTRACTS: AssistantMcpCatalogPrimitiveContract[] = [
primitive_id: "explain_evidence_basis",
purpose: "Produce a machine-readable explanation of which checked MCP evidence supports, limits, or fails the answer.",
decomposition_hints: ["explain_evidence_basis"],
supported_fact_families: ["activity_lifecycle"],
supported_action_families: ["activity_duration"],
planning_tags: ["explanation"],
required_axes_any_of: [["evidence_basis"], ["primitive_id"], ["source_rows_summary"]],
optional_axes: ["coverage_target", "domain_family"],
output_fact_kinds: ["confirmed_facts", "inferred_facts", "unknown_facts"],
@@ -183,11 +233,192 @@ function normalizeDecompositionStep(value: string): string {
return value.trim().toLowerCase();
}
function normalizePlanningToken(value: string): string {
return value.trim().toLowerCase();
}
function countAxisOverlap(axisSet: Set<string>, groups: string[][]): number {
let best = 0;
for (const group of groups) {
const overlap = group.filter((axis) => axisSet.has(axis)).length;
if (overlap > best) {
best = overlap;
}
}
return best;
}
function tagSetFromFactAxisInput(input: AssistantMcpCatalogFactAxisSearchInput): Set<string> {
const tags = new Set<string>();
const requiredAxes = input.required_axes ?? [];
if (input.business_fact_family === "schema_surface") {
tags.add("metadata");
tags.add("surface_inspection");
}
if (
input.business_fact_family === "entity_grounding" ||
input.has_subject_candidates ||
requiredAxes.some((axis) => ["business_entity", "counterparty", "contract", "item"].includes(axis))
) {
tags.add("subject_resolution");
}
if (input.business_fact_family === "document_evidence") {
tags.add("document");
}
if (input.business_fact_family === "movement_evidence") {
tags.add("movement");
}
if (input.business_fact_family === "value_flow") {
tags.add("movement");
}
if (input.comparison_need) {
tags.add("comparison");
}
if (input.ranking_need) {
tags.add("ranking");
tags.add("aggregation");
}
if ((input.aggregation_need ?? "") === "by_month" || requiredAxes.includes("calendar_month")) {
tags.add("monthly_aggregation");
tags.add("aggregation");
}
if (requiredAxes.includes("aggregate_axis")) {
tags.add("aggregation");
}
if (requiredAxes.includes("coverage_target")) {
tags.add("coverage");
}
if (input.business_fact_family === "activity_lifecycle" || requiredAxes.includes("evidence_basis")) {
tags.add("explanation");
}
return tags;
}
function matchesPlanningToken(value: string | null | undefined, candidates: string[]): boolean {
const normalizedValue = normalizePlanningToken(value ?? "");
return normalizedValue.length > 0 && candidates.some((candidate) => normalizePlanningToken(candidate) === normalizedValue);
}
export interface AssistantMcpCatalogFactAxisSearchInput {
business_fact_family?: string | null;
action_family?: string | null;
required_axes?: string[];
comparison_need?: string | null;
ranking_need?: string | null;
aggregation_need?: string | null;
has_subject_candidates?: boolean;
allow_aggregate_by_axis?: boolean;
}
export interface AssistantMcpCatalogPrimitiveSearchInput {
decomposition_candidates: string[];
allow_aggregate_by_axis?: boolean;
}
export interface AssistantMcpCatalogMetadataSurfaceSearchInput {
downstream_route_family?: string | null;
selected_entity_set?: string | null;
selected_surface_objects?: string[];
recommended_next_primitive?: string | null;
required_axes?: string[];
allow_aggregate_by_axis?: boolean;
}
function normalizeMetadataSurfaceToken(value: string): string {
return value.trim().toLowerCase().replace(/[\s_.-]+/g, "");
}
function metadataSurfaceSuggestsDocument(value: string): boolean {
const token = normalizeMetadataSurfaceToken(value);
return (
token.includes("документ") ||
token.includes("document") ||
token.includes("invoice") ||
token.includes("waybill") ||
token.includes("накладн") ||
token.includes("счетфактур") ||
token.includes("счётфактур") ||
token.includes("акт")
);
}
function metadataSurfaceSuggestsMovement(value: string): boolean {
const token = normalizeMetadataSurfaceToken(value);
return (
token.includes("регистр") ||
token.includes("register") ||
token.includes("movement") ||
token.includes("движени") ||
token.includes("операц") ||
token.includes("проводк") ||
token.includes("bank")
);
}
function metadataSurfaceSuggestsCatalog(value: string): boolean {
const token = normalizeMetadataSurfaceToken(value);
return (
token.includes("справочник") ||
token.includes("catalog") ||
token.includes("directory")
);
}
function tagSetFromMetadataSurfaceInput(input: AssistantMcpCatalogMetadataSurfaceSearchInput): Set<string> {
const tags = new Set<string>();
const routeFamily = normalizePlanningToken(input.downstream_route_family ?? "");
const recommendedPrimitive = normalizePlanningToken(input.recommended_next_primitive ?? "");
const surfaceValues = [
input.selected_entity_set ?? "",
...(input.selected_surface_objects ?? [])
];
if (routeFamily === "document_evidence" || recommendedPrimitive === "query_documents") {
tags.add("document");
}
if (routeFamily === "movement_evidence" || recommendedPrimitive === "query_movements") {
tags.add("movement");
}
if (routeFamily === "catalog_drilldown" || recommendedPrimitive === "drilldown_related_objects") {
tags.add("drilldown");
}
for (const value of surfaceValues) {
if (metadataSurfaceSuggestsDocument(value)) {
tags.add("document");
}
if (metadataSurfaceSuggestsMovement(value)) {
tags.add("movement");
}
if (metadataSurfaceSuggestsCatalog(value)) {
tags.add("drilldown");
}
}
const requiredAxisSet = toStringSet(input.required_axes ?? []);
if (requiredAxisSet.has("coverage_target")) {
tags.add("coverage");
}
if (
requiredAxisSet.has("counterparty") ||
requiredAxisSet.has("business_entity") ||
requiredAxisSet.has("contract")
) {
tags.add("subject_resolution");
}
return tags;
}
function factFamiliesFromMetadataSurfaceInput(input: AssistantMcpCatalogMetadataSurfaceSearchInput): Set<string> {
const families = new Set<string>();
const routeFamily = normalizePlanningToken(input.downstream_route_family ?? "");
if (routeFamily === "document_evidence") {
families.add("document_evidence");
}
if (routeFamily === "movement_evidence") {
families.add("movement_evidence");
}
return families;
}
export function searchAssistantMcpCatalogPrimitivesByDecompositionCandidates(
input: AssistantMcpCatalogPrimitiveSearchInput
): AssistantMcpDiscoveryPrimitive[] {
@@ -220,6 +451,135 @@ export function searchAssistantMcpCatalogPrimitivesByDecompositionCandidates(
return result;
}
export function searchAssistantMcpCatalogPrimitivesByFactAxis(
input: AssistantMcpCatalogFactAxisSearchInput
): AssistantMcpDiscoveryPrimitive[] {
const allowAggregateByAxis = input.allow_aggregate_by_axis !== false;
const requiredAxisSet = toStringSet(input.required_axes ?? []);
const desiredTags = tagSetFromFactAxisInput(input);
const scored: Array<{ primitive: AssistantMcpDiscoveryPrimitive; score: number }> = [];
for (const contract of PRIMITIVE_CONTRACTS) {
if (contract.primitive_id === "aggregate_by_axis" && !allowAggregateByAxis) {
continue;
}
if (contract.primitive_id === "search_business_entity" && input.business_fact_family !== "entity_grounding") {
continue;
}
if (input.business_fact_family === "schema_surface" && contract.primitive_id !== "inspect_1c_metadata") {
continue;
}
const factMatch = matchesPlanningToken(input.business_fact_family, contract.supported_fact_families);
const actionMatch = matchesPlanningToken(input.action_family, contract.supported_action_families);
const tagMatches = contract.planning_tags.filter((tag) => desiredTags.has(normalizePlanningToken(tag)));
if (contract.primitive_id === "search_business_entity" && !desiredTags.has("subject_resolution")) {
continue;
}
if (contract.primitive_id === "resolve_entity_reference" && !desiredTags.has("subject_resolution")) {
continue;
}
if (contract.primitive_id === "aggregate_by_axis" && !desiredTags.has("aggregation")) {
continue;
}
if (contract.primitive_id === "explain_evidence_basis" && !desiredTags.has("explanation")) {
continue;
}
if (!factMatch && !actionMatch && tagMatches.length <= 0) {
continue;
}
const hasCompatibleAxisGroup = requiredAxisSet.size > 0 && hasAnyAxisGroup(requiredAxisSet, contract.required_axes_any_of);
const axisOverlap = requiredAxisSet.size > 0 ? countAxisOverlap(requiredAxisSet, contract.required_axes_any_of) : 0;
let score = 0;
if (factMatch) {
score += 5;
}
if (actionMatch) {
score += 3;
}
score += tagMatches.length * 2;
if (hasCompatibleAxisGroup) {
score += 2;
} else if (axisOverlap > 0) {
score += 1;
}
if (score <= 0) {
continue;
}
scored.push({
primitive: contract.primitive_id,
score
});
}
return scored
.sort((left, right) => right.score - left.score)
.map((item) => item.primitive);
}
export function searchAssistantMcpCatalogPrimitivesByMetadataSurface(
input: AssistantMcpCatalogMetadataSurfaceSearchInput
): AssistantMcpDiscoveryPrimitive[] {
const allowAggregateByAxis = input.allow_aggregate_by_axis !== false;
const requiredAxisSet = toStringSet(input.required_axes ?? []);
const desiredTags = tagSetFromMetadataSurfaceInput(input);
const desiredFactFamilies = factFamiliesFromMetadataSurfaceInput(input);
const recommendedPrimitive = normalizePlanningToken(input.recommended_next_primitive ?? "");
const scored: Array<{ primitive: AssistantMcpDiscoveryPrimitive; score: number }> = [];
for (const contract of PRIMITIVE_CONTRACTS) {
if (contract.primitive_id === "aggregate_by_axis" && !allowAggregateByAxis) {
continue;
}
if (contract.primitive_id === "search_business_entity") {
continue;
}
const tagMatches = contract.planning_tags.filter((tag) => desiredTags.has(normalizePlanningToken(tag)));
const factMatch = contract.supported_fact_families.some((family) =>
desiredFactFamilies.has(normalizePlanningToken(family))
);
const primitiveRecommended = normalizePlanningToken(contract.primitive_id) === recommendedPrimitive;
if (contract.primitive_id === "resolve_entity_reference" && !desiredTags.has("subject_resolution")) {
continue;
}
if (contract.primitive_id === "aggregate_by_axis" && !desiredTags.has("aggregation")) {
continue;
}
if (!primitiveRecommended && !factMatch && tagMatches.length <= 0) {
continue;
}
const hasCompatibleAxisGroup = requiredAxisSet.size > 0 && hasAnyAxisGroup(requiredAxisSet, contract.required_axes_any_of);
const axisOverlap = requiredAxisSet.size > 0 ? countAxisOverlap(requiredAxisSet, contract.required_axes_any_of) : 0;
let score = 0;
if (primitiveRecommended) {
score += 6;
}
if (factMatch) {
score += 5;
}
score += tagMatches.length * 2;
if (hasCompatibleAxisGroup) {
score += 2;
} else if (axisOverlap > 0) {
score += 1;
}
if (score <= 0) {
continue;
}
scored.push({
primitive: contract.primitive_id,
score
});
}
return scored
.sort((left, right) => right.score - left.score)
.map((item) => item.primitive);
}
export function buildAssistantMcpCatalogIndex(): AssistantMcpCatalogIndexContract {
const reasonCodes: string[] = [];
const missingContracts = ASSISTANT_MCP_DISCOVERY_PRIMITIVES.filter((primitive) => !PRIMITIVE_CONTRACT_MAP.has(primitive));
@@ -130,7 +130,9 @@ function isEntityResolutionPilot(pilot: AssistantMcpDiscoveryPilotExecutionContr
function isMetadataLaneChoiceClarification(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean {
return (
pilot.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe") ||
pilot.dry_run.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe")
pilot.reason_codes.includes("planner_selected_metadata_lane_clarification_from_data_need_graph") ||
pilot.dry_run.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe") ||
pilot.dry_run.reason_codes.includes("planner_selected_metadata_lane_clarification_from_data_need_graph")
);
}
@@ -109,13 +109,29 @@ function aggregationNeedFor(axis: string): string | null {
return `by_${axis}`;
}
function hasAllTimeScopeHint(rawUtterance: string): boolean {
if (!rawUtterance) {
return false;
}
return /(?:\u0437\u0430\s+\u0432\u0441[\u0435\u0451]\s+\u0432\u0440\u0435\u043c\u044f|\u0437\u0430\s+\u0432\u0435\u0441\u044c\s+\u043f\u0435\u0440\u0438\u043e\u0434|\u0437\u0430\s+\u0432\u0441\u044e\s+\u0438\u0441\u0442\u043e\u0440\u0438(?:\u044e|\u0438)|\u0437\u0430\s+\u043b\u044e\u0431\u043e\u0439\s+\u043f\u0435\u0440\u0438\u043e\u0434|for\s+all\s+time|all\s+time|entire\s+period|full\s+history|any\s+period)/iu.test(
rawUtterance
);
}
function timeScopeNeedFor(input: {
family: string | null;
explicitDateScope: string | null;
allTimeScopeHint: boolean;
}): string | null {
if (input.explicitDateScope) {
return "explicit_period";
}
if (
input.allTimeScopeHint &&
(input.family === "value_flow" || input.family === "movement_evidence" || input.family === "document_evidence")
) {
return "all_time_scope";
}
if (input.family === "value_flow" || input.family === "movement_evidence" || input.family === "document_evidence") {
return "period_required";
}
@@ -341,6 +357,7 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
const aggregationNeed = aggregationNeedFor(aggregationAxis);
const comparisonNeed = comparisonNeedFor(action);
const rankingNeed = rankingNeedFromRawUtterance(rawUtterance) ?? seededRankingNeed;
const allTimeScopeHint = hasAllTimeScopeHint(rawUtterance);
const oneSidedOpenScopeTotalHint = hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, action);
const openScopeWithoutSubject =
subjectCandidates.length === 0 &&
@@ -368,7 +385,8 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
}
const timeScopeNeed = timeScopeNeedFor({
family: businessFactFamily,
explicitDateScope
explicitDateScope,
allTimeScopeHint
});
if (timeScopeNeed === "period_required" && !explicitDateScope) {
pushUnique(clarificationGaps, "period");
@@ -400,6 +418,9 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
if (openScopeWithoutSubject && !rankingNeed && !comparisonNeed) {
pushReason(reasonCodes, "data_need_graph_open_scope_total_without_subject");
}
if (allTimeScopeHint) {
pushReason(reasonCodes, "data_need_graph_all_time_scope_hint");
}
if (clarificationGaps.includes("organization")) {
pushReason(reasonCodes, "data_need_graph_open_scope_total_needs_organization");
}
@@ -149,7 +149,13 @@ export interface AssistantMcpDiscoveryDerivedMetadataSurface {
matched_objects: string[];
selected_entity_set: string | null;
selected_surface_objects: string[];
surface_family_scores: {
document_evidence: number;
movement_evidence: number;
catalog_drilldown: number;
};
downstream_route_family: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null;
route_family_selection_basis: "selected_entity_set" | "dominant_surface_objects" | null;
recommended_next_primitive: "query_documents" | "query_movements" | "drilldown_related_objects" | null;
ambiguity_detected: boolean;
ambiguity_entity_sets: string[];
@@ -1193,6 +1199,34 @@ function metadataRouteFamilyForEntitySet(
return null;
}
function metadataRouteFamilyForEntitySetRelaxed(
entitySet: string
): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null {
const strict = metadataRouteFamilyForEntitySet(entitySet);
if (strict) {
return strict;
}
const raw = String(entitySet ?? "").trim();
if (!raw) {
return null;
}
if (raw.includes("Документ") || raw.includes("Документ")) {
return "document_evidence";
}
if (
raw.includes("РегистрНакопления") ||
raw.includes("РегистрСведений") ||
raw.includes("РегистрНакопления") ||
raw.includes("РегистрСведений")
) {
return "movement_evidence";
}
if (raw.includes("Справочник") || raw.includes("Справочник")) {
return "catalog_drilldown";
}
return null;
}
function metadataNextPrimitiveForRouteFamily(
routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null
): "query_documents" | "query_movements" | "drilldown_related_objects" | null {
@@ -1254,6 +1288,63 @@ function metadataObjectsForEntitySet(entitySet: string | null, matchedObjects: s
return matchedObjects.filter((item) => item.startsWith(`${entitySet}.`) || item.includes(entitySet));
}
function emptyMetadataSurfaceFamilyScores(): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] {
return {
document_evidence: 0,
movement_evidence: 0,
catalog_drilldown: 0
};
}
function metadataSurfaceFamilyScores(
matchedObjects: string[]
): AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"] {
const scores = emptyMetadataSurfaceFamilyScores();
for (const objectName of matchedObjects) {
const entitySet = inferMetadataEntitySetFromObjectName(objectName);
const routeFamily = entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) : null;
if (routeFamily) {
scores[routeFamily] += 1;
}
}
return scores;
}
function metadataObjectsForRouteFamily(
routeFamily: "document_evidence" | "movement_evidence" | "catalog_drilldown" | null,
matchedObjects: string[]
): string[] {
if (!routeFamily) {
return [];
}
return matchedObjects.filter((objectName) => {
const entitySet = inferMetadataEntitySetFromObjectName(objectName);
return entitySet ? metadataRouteFamilyForEntitySetRelaxed(entitySet) === routeFamily : false;
});
}
function selectMetadataRouteFamilyFromSurfaceScores(
scores: AssistantMcpDiscoveryDerivedMetadataSurface["surface_family_scores"]
): "document_evidence" | "movement_evidence" | "catalog_drilldown" | null {
const ranked = (Object.entries(scores) as Array<
["document_evidence" | "movement_evidence" | "catalog_drilldown", number]
>)
.filter(([, score]) => score > 0)
.sort((left, right) => right[1] - left[1]);
const top = ranked[0];
const second = ranked[1];
if (!top) {
return null;
}
if (!second) {
return top[0];
}
const absoluteMargin = top[1] - second[1];
const relativeRatio = second[1] > 0 ? top[1] / second[1] : Number.POSITIVE_INFINITY;
const clearlyDominant = absoluteMargin >= 2 || relativeRatio >= 1.5;
return clearlyDominant ? top[0] : null;
}
function deriveMetadataSurface(
result: AddressMcpMetadataRowsResult | null,
metadataScope: string | null,
@@ -1275,15 +1366,34 @@ function deriveMetadataSurface(
}
}
const grounding = selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes);
const downstreamRouteFamily = grounding.selectedEntitySet
? metadataRouteFamilyForEntitySet(grounding.selectedEntitySet)
const surfaceFamilyScores = metadataSurfaceFamilyScores(matchedObjects);
const selectedEntitySetRouteFamily = grounding.selectedEntitySet
? metadataRouteFamilyForEntitySetRelaxed(grounding.selectedEntitySet)
: null;
const scoredRouteFamily =
selectedEntitySetRouteFamily === null ? selectMetadataRouteFamilyFromSurfaceScores(surfaceFamilyScores) : null;
const downstreamRouteFamily = selectedEntitySetRouteFamily ?? scoredRouteFamily;
const routeFamilySelectionBasis = selectedEntitySetRouteFamily
? "selected_entity_set"
: scoredRouteFamily
? "dominant_surface_objects"
: null;
const selectedSurfaceObjects =
grounding.selectedEntitySet !== null
? metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects)
: metadataObjectsForRouteFamily(downstreamRouteFamily, matchedObjects);
const knownLimitations: string[] = [];
if (grounding.ambiguityDetected && grounding.ambiguityEntitySets.length > 0) {
const ambiguityRemainsUnresolved = grounding.ambiguityDetected && !downstreamRouteFamily;
if (ambiguityRemainsUnresolved && grounding.ambiguityEntitySets.length > 0) {
knownLimitations.push(
`Exact downstream metadata surface remains ambiguous across: ${grounding.ambiguityEntitySets.join(", ")}`
);
}
if (grounding.ambiguityDetected && downstreamRouteFamily && routeFamilySelectionBasis === "dominant_surface_objects") {
knownLimitations.push(
`Metadata surface spans multiple object sets, but dominant confirmed objects point to ${downstreamRouteFamily}`
);
}
return {
metadata_scope: metadataScope,
requested_meta_types: requestedMetaTypes,
@@ -1291,11 +1401,13 @@ function deriveMetadataSurface(
available_entity_sets: availableEntitySets,
matched_objects: matchedObjects,
selected_entity_set: grounding.selectedEntitySet,
selected_surface_objects: metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects),
selected_surface_objects: selectedSurfaceObjects,
surface_family_scores: surfaceFamilyScores,
downstream_route_family: downstreamRouteFamily,
route_family_selection_basis: routeFamilySelectionBasis,
recommended_next_primitive: metadataNextPrimitiveForRouteFamily(downstreamRouteFamily),
ambiguity_detected: grounding.ambiguityDetected,
ambiguity_entity_sets: grounding.ambiguityEntitySets,
ambiguity_detected: ambiguityRemainsUnresolved,
ambiguity_entity_sets: ambiguityRemainsUnresolved ? grounding.ambiguityEntitySets : [],
available_fields: metadataAvailableFields(result.rows),
known_limitations: knownLimitations,
inference_basis: "confirmed_1c_metadata_surface_rows"
@@ -1322,6 +1434,15 @@ function buildMetadataConfirmedFacts(
if (surface.selected_surface_objects.length > 0) {
facts.push(`Selected metadata objects: ${surface.selected_surface_objects.slice(0, 8).join(", ")}`);
}
if (
surface.surface_family_scores.document_evidence > 0 ||
surface.surface_family_scores.movement_evidence > 0 ||
surface.surface_family_scores.catalog_drilldown > 0
) {
facts.push(
`Metadata surface family scores: document=${surface.surface_family_scores.document_evidence}, movement=${surface.surface_family_scores.movement_evidence}, catalog=${surface.surface_family_scores.catalog_drilldown}`
);
}
if (surface.available_fields.length > 0) {
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
}
@@ -1331,7 +1452,7 @@ function buildMetadataConfirmedFacts(
function buildMetadataInferredFacts(
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null
): string[] {
if (!surface || !surface.selected_entity_set || !surface.downstream_route_family || !surface.recommended_next_primitive) {
if (!surface || !surface.downstream_route_family || !surface.recommended_next_primitive) {
return [];
}
return [
@@ -2304,6 +2425,9 @@ export async function executeAssistantMcpDiscoveryPilot(
const derivedMetadataSurface = deriveMetadataSurface(metadataResult, metadataScope, requestedMetaTypes);
if (derivedMetadataSurface) {
pushReason(reasonCodes, "pilot_derived_metadata_surface_from_confirmed_rows");
if (derivedMetadataSurface.route_family_selection_basis === "dominant_surface_objects") {
pushReason(reasonCodes, "pilot_selected_metadata_route_family_from_dominant_surface_objects");
}
}
const evidence = resolveAssistantMcpDiscoveryEvidence({
plan: planner.discovery_plan,
@@ -6,6 +6,8 @@ import {
} from "./assistantMcpDiscoveryPolicy";
import {
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
searchAssistantMcpCatalogPrimitivesByFactAxis,
searchAssistantMcpCatalogPrimitivesByMetadataSurface,
reviewAssistantMcpDiscoveryPlanAgainstCatalog,
type AssistantMcpCatalogPlanReview
} from "./assistantMcpCatalogIndex";
@@ -14,6 +16,24 @@ import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpD
export const ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1" as const;
export type AssistantMcpDiscoveryPlannerStatus = "ready_for_execution" | "needs_clarification" | "blocked";
export type AssistantMcpDiscoveryMetadataRouteFamily =
| "document_evidence"
| "movement_evidence"
| "catalog_drilldown";
export type AssistantMcpDiscoveryMetadataRecommendedPrimitive =
| "query_documents"
| "query_movements"
| "drilldown_related_objects";
export interface AssistantMcpDiscoveryMetadataSurfaceRef {
selected_entity_set: string | null;
selected_surface_objects: string[];
downstream_route_family: AssistantMcpDiscoveryMetadataRouteFamily | null;
route_family_selection_basis: "selected_entity_set" | "dominant_surface_objects" | null;
recommended_next_primitive: AssistantMcpDiscoveryMetadataRecommendedPrimitive | null;
ambiguity_detected: boolean;
ambiguity_entity_sets: string[];
}
export type AssistantMcpDiscoveryChainId =
| "metadata_inspection"
@@ -29,6 +49,7 @@ export type AssistantMcpDiscoveryChainId =
export interface AssistantMcpDiscoveryPlannerInput {
semanticDataNeed?: string | null;
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
turnMeaning?: AssistantMcpDiscoveryTurnMeaningRef | null;
}
@@ -38,6 +59,7 @@ export interface AssistantMcpDiscoveryPlannerContract {
planner_status: AssistantMcpDiscoveryPlannerStatus;
semantic_data_need: string | null;
data_need_graph: AssistantMcpDiscoveryDataNeedGraphContract | null;
metadata_surface_ref: AssistantMcpDiscoveryMetadataSurfaceRef | null;
selected_chain_id: AssistantMcpDiscoveryChainId;
selected_chain_summary: string;
proposed_primitives: AssistantMcpDiscoveryPrimitive[];
@@ -135,43 +157,185 @@ function isYearDateScope(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | u
return /^\d{4}$/.test(toNonEmptyString(meaning?.explicit_date_scope) ?? "");
}
function primitivesFromGraphDecomposition(input: {
dataNeedGraph: AssistantMcpDiscoveryDataNeedGraphContract | null;
function mergeCatalogPrimitivesWithFallback(
catalogPrimitives: AssistantMcpDiscoveryPrimitive[],
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[]
): AssistantMcpDiscoveryPrimitive[] {
const result: AssistantMcpDiscoveryPrimitive[] = [];
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: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
): AssistantMcpDiscoveryPrimitive | null {
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: {
catalogPrimitives: AssistantMcpDiscoveryPrimitive[];
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[];
allowAggregateByAxis?: boolean;
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
const decompositionCandidates = input.dataNeedGraph?.decomposition_candidates ?? [];
if (decompositionCandidates.length <= 0) {
return { primitives: input.fallbackPrimitives, reasonCodes: [] };
const preferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
const reasonCodes: string[] = [];
if (
!preferredPrimitive ||
input.metadataSurface?.ambiguity_detected ||
!input.fallbackPrimitives.includes(preferredPrimitive)
) {
return {
primitives: input.catalogPrimitives,
reasonCodes
};
}
const searchedPrimitives = searchAssistantMcpCatalogPrimitivesByDecompositionCandidates({
decomposition_candidates: decompositionCandidates,
allow_aggregate_by_axis: input.allowAggregateByAxis
const laneSensitivePrimitives = new Set<AssistantMcpDiscoveryPrimitive>([
"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: {
dataNeedGraph: AssistantMcpDiscoveryDataNeedGraphContract | null;
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[];
requiredAxes: string[];
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
actionFamily?: string | null;
allowAggregateByAxis?: boolean;
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
const reasonCodes: string[] = [];
const decompositionCandidates = input.dataNeedGraph?.decomposition_candidates ?? [];
const decompositionPrimitives =
decompositionCandidates.length > 0
? 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
? 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
? 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: AssistantMcpDiscoveryPrimitive[] = [];
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
});
if (searchedPrimitives.length <= 0) {
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 = [...searchedPrimitives];
for (const primitive of input.fallbackPrimitives) {
if (!mergedPrimitives.includes(primitive)) {
mergedPrimitives.push(primitive);
}
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:
mergedPrimitives.length === searchedPrimitives.length
? ["planner_selected_catalog_primitives_from_decomposition_candidates"]
: [
"planner_selected_catalog_primitives_from_decomposition_candidates",
"planner_completed_catalog_searched_chain_with_recipe_primitives"
]
reasonCodes
};
}
@@ -192,6 +356,40 @@ function budgetOverrideFor(input: AssistantMcpDiscoveryPlannerInput, recipe: Pla
return {};
}
function routeFamilyFromThinMetadataSurfaceInput(
input: AssistantMcpDiscoveryPlannerInput
): AssistantMcpDiscoveryMetadataRouteFamily | null {
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 && !domain && !action) {
if (surface.downstream_route_family === "document_evidence" || surface.downstream_route_family === "movement_evidence") {
return surface.downstream_route_family;
}
}
return null;
}
function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
const meaning = input.turnMeaning ?? null;
const dataNeedGraph = input.dataNeedGraph ?? null;
@@ -224,18 +422,63 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
};
}
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 (graphFactFamily === "value_flow") {
if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing" && !hasSubjectCandidates(dataNeedGraph)) {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "probe_coverage"],
allowAggregateByAxis: false
});
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",
@@ -248,14 +491,17 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
};
}
if (dataNeedGraph?.ranking_need && !hasSubjectCandidates(dataNeedGraph)) {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
allowAggregateByAxis: true
});
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",
@@ -271,11 +517,6 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
};
}
if (openScopeTotalWithoutSubject) {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
allowAggregateByAxis: true
});
pushUnique(axes, "organization");
pushUnique(axes, "aggregate_axis");
pushUnique(axes, "amount");
@@ -283,6 +524,13 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
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",
@@ -297,17 +545,20 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
extraReasons: primitiveSelection.reasonCodes
};
}
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
allowAggregateByAxis: true
});
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",
@@ -323,13 +574,16 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
}
if (graphFactFamily === "activity_lifecycle") {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"]
});
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",
@@ -342,11 +596,14 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
}
if (graphFactFamily === "schema_surface") {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["inspect_1c_metadata"]
});
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",
@@ -359,11 +616,14 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
}
if (graphFactFamily === "movement_evidence") {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_movements", "probe_coverage"]
});
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",
@@ -376,11 +636,14 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
}
if (graphFactFamily === "document_evidence") {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["resolve_entity_reference", "query_documents", "probe_coverage"]
});
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",
@@ -393,12 +656,15 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
}
if (graphFactFamily === "entity_grounding" || (!graphFactFamily && (dataNeedGraph?.subject_candidates.length ?? 0) > 0)) {
const primitiveSelection = primitivesFromGraphDecomposition({
dataNeedGraph,
fallbackPrimitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"]
});
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",
@@ -537,6 +803,7 @@ export function planAssistantMcpDiscovery(
const budgetOverride = budgetOverrideFor(input, recipe);
const semanticDataNeed = toNonEmptyString(input.semanticDataNeed) ?? recipe.semanticDataNeed;
const dataNeedGraph = input.dataNeedGraph ?? null;
const metadataSurface = input.metadataSurface ?? null;
const reasonCodes: string[] = [];
pushReason(reasonCodes, recipe.reason);
for (const reason of recipe.extraReasons ?? []) {
@@ -545,6 +812,9 @@ export function planAssistantMcpDiscovery(
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");
}
@@ -596,12 +866,13 @@ export function planAssistantMcpDiscovery(
planner_status: plannerStatus,
semantic_data_need: semanticDataNeed,
data_need_graph: dataNeedGraph,
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
};
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
};
}
@@ -9,6 +9,7 @@ import {
} from "./assistantMcpDiscoveryPilotExecutor";
import {
planAssistantMcpDiscovery,
type AssistantMcpDiscoveryMetadataSurfaceRef,
type AssistantMcpDiscoveryPlannerContract
} from "./assistantMcpDiscoveryPlanner";
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
@@ -27,6 +28,7 @@ export type AssistantMcpDiscoveryRuntimeBridgeStatus =
export interface AssistantMcpDiscoveryRuntimeBridgeInput {
semanticDataNeed?: string | null;
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
turnMeaning?: AssistantMcpDiscoveryTurnMeaningRef | null;
deps?: AssistantMcpDiscoveryPilotExecutorDeps;
}
@@ -101,6 +103,7 @@ export async function runAssistantMcpDiscoveryRuntimeBridge(
const planner = planAssistantMcpDiscovery({
semanticDataNeed: input.semanticDataNeed,
dataNeedGraph: input.dataNeedGraph,
metadataSurface: input.metadataSurface,
turnMeaning: input.turnMeaning
});
const pilot = await executeAssistantMcpDiscoveryPilot(planner, input.deps);
@@ -102,6 +102,7 @@ export async function runAssistantMcpDiscoveryRuntimeEntryPoint(
const bridge = await runAssistantMcpDiscoveryRuntimeBridge({
semanticDataNeed: turnInput.semantic_data_need,
dataNeedGraph: turnInput.data_need_graph,
metadataSurface: turnInput.metadata_surface_ref,
turnMeaning: turnInput.turn_meaning_ref,
deps: input.deps
});
@@ -3,6 +3,11 @@ import {
buildAssistantMcpDiscoveryDataNeedGraph,
type AssistantMcpDiscoveryDataNeedGraphContract
} from "./assistantMcpDiscoveryDataNeedGraph";
import type {
AssistantMcpDiscoveryMetadataRecommendedPrimitive,
AssistantMcpDiscoveryMetadataRouteFamily,
AssistantMcpDiscoveryMetadataSurfaceRef
} from "./assistantMcpDiscoveryPlanner";
export const ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION =
"assistant_mcp_discovery_turn_input_v1" as const;
@@ -30,6 +35,7 @@ export interface AssistantMcpDiscoveryTurnInputContract {
should_run_discovery: boolean;
semantic_data_need: string | null;
data_need_graph: AssistantMcpDiscoveryDataNeedGraphContract | null;
metadata_surface_ref: AssistantMcpDiscoveryMetadataSurfaceRef | null;
turn_meaning_ref: AssistantMcpDiscoveryTurnMeaningRef | null;
source_signal: AssistantMcpDiscoveryTurnInputSource;
reason_codes: string[];
@@ -203,6 +209,31 @@ function collectDateScopeFromFilters(filters: Record<string, unknown> | null): s
return periodFrom ?? periodTo ?? null;
}
function normalizeMetadataRouteFamily(value: unknown): AssistantMcpDiscoveryMetadataRouteFamily | null {
const text = toNonEmptyString(value);
if (text === "document_evidence" || text === "movement_evidence" || text === "catalog_drilldown") {
return text;
}
return null;
}
function normalizeMetadataRecommendedPrimitive(
value: unknown
): AssistantMcpDiscoveryMetadataRecommendedPrimitive | null {
const text = toNonEmptyString(value);
if (text === "query_documents" || text === "query_movements" || text === "drilldown_related_objects") {
return text;
}
return null;
}
function normalizeMetadataRouteFamilySelectionBasis(
value: unknown
): AssistantMcpDiscoveryMetadataSurfaceRef["route_family_selection_basis"] {
const text = toNonEmptyString(value);
return text === "selected_entity_set" || text === "dominant_surface_objects" ? text : null;
}
function mapPilotScopeToFollowupMeaning(
pilotScope: string | null
): {
@@ -320,8 +351,11 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
rankingNeed: string | null;
organization: string | null;
dateScope: string | null;
metadataRouteFamily: string | null;
metadataRouteFamily: AssistantMcpDiscoveryMetadataRouteFamily | null;
metadataRouteFamilySelectionBasis: AssistantMcpDiscoveryMetadataSurfaceRef["route_family_selection_basis"];
metadataSelectedEntitySet: string | null;
metadataSelectedSurfaceObjects: string[];
metadataRecommendedNextPrimitive: AssistantMcpDiscoveryMetadataRecommendedPrimitive | null;
metadataAmbiguityDetected: boolean;
metadataAmbiguityEntitySets: string[];
} {
@@ -369,13 +403,52 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
rankingNeed: toNonEmptyString(followupContext?.previous_discovery_ranking_need),
organization,
dateScope,
metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family),
metadataRouteFamily: normalizeMetadataRouteFamily(followupContext?.previous_discovery_metadata_route_family),
metadataRouteFamilySelectionBasis: normalizeMetadataRouteFamilySelectionBasis(
followupContext?.previous_discovery_metadata_route_family_selection_basis
),
metadataSelectedEntitySet: toNonEmptyString(followupContext?.previous_discovery_metadata_selected_entity_set),
metadataSelectedSurfaceObjects: collectEntityCandidates(
followupContext?.previous_discovery_metadata_selected_surface_objects
),
metadataRecommendedNextPrimitive: normalizeMetadataRecommendedPrimitive(
followupContext?.previous_discovery_metadata_recommended_next_primitive
),
metadataAmbiguityDetected: followupContext?.previous_discovery_metadata_ambiguity_detected === true,
metadataAmbiguityEntitySets: collectEntityCandidates(followupContext?.previous_discovery_metadata_ambiguity_entity_sets)
};
}
function buildMetadataSurfaceRef(
followupSeed: ReturnType<typeof collectFollowupDiscoverySeed>
): AssistantMcpDiscoveryMetadataSurfaceRef | null {
if (followupSeed.pilotScope !== "metadata_inspection_v1") {
return null;
}
const hasPayload =
Boolean(
followupSeed.metadataRouteFamily ||
followupSeed.metadataSelectedEntitySet ||
followupSeed.metadataRecommendedNextPrimitive ||
followupSeed.metadataRouteFamilySelectionBasis
) ||
followupSeed.metadataSelectedSurfaceObjects.length > 0 ||
followupSeed.metadataAmbiguityDetected ||
followupSeed.metadataAmbiguityEntitySets.length > 0;
if (!hasPayload) {
return null;
}
return {
selected_entity_set: followupSeed.metadataSelectedEntitySet,
selected_surface_objects: followupSeed.metadataSelectedSurfaceObjects,
downstream_route_family: followupSeed.metadataRouteFamily,
route_family_selection_basis: followupSeed.metadataRouteFamilySelectionBasis,
recommended_next_primitive: followupSeed.metadataRecommendedNextPrimitive,
ambiguity_detected: followupSeed.metadataAmbiguityDetected,
ambiguity_entity_sets: followupSeed.metadataAmbiguityEntitySets
};
}
function metadataEntitySetsSuggestDocumentLane(values: string[]): boolean {
return values.some((value) => /(?:документ|document|invoice|waybill|накладн|счет[- ]?фактур|акт)/iu.test(value));
}
@@ -401,7 +474,7 @@ function hasLifecycleSignal(text: string): boolean {
}
function hasValueFlowSignal(text: string): boolean {
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|входящ|получ(?:ил|ено|ен)|поступил|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|входящ|получ(?:ил|ено|ен)|поступил|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|(?<!\p{L})заработ(?:ал|али|ало|аем|ает|ать|ано|ок)(?!\p{L})|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow|\bearn(?:ed|ing|ings)?\b)/iu.test(
text
);
}
@@ -458,6 +531,12 @@ function hasMonthlyAggregationSignal(text: string): boolean {
);
}
function hasAllTimeScopeHint(text: string): boolean {
return /(?:\u0437\u0430\s+\u0432\u0441[\u0435\u0451]\s+\u0432\u0440\u0435\u043c\u044f|\u0437\u0430\s+\u0432\u0435\u0441\u044c\s+\u043f\u0435\u0440\u0438\u043e\u0434|\u0437\u0430\s+\u0432\u0441\u044e\s+\u0438\u0441\u0442\u043e\u0440\u0438(?:\u044e|\u0438)|\u0437\u0430\s+\u043b\u044e\u0431\u043e\u0439\s+\u043f\u0435\u0440\u0438\u043e\u0434|for\s+all\s+time|all\s+time|entire\s+period|full\s+history|any\s+period)/iu.test(
text
);
}
function hasMetadataSignal(text: string): boolean {
if (
/(?:\u043c\u0435\u0442\u0430\u0434\u0430\u043d|schema|catalog|metadata\s+surface|\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440[\u0430\u044b]\s+1\u0441|\u0441\u0445\u0435\u043c[\u0430\u044b]\s+1\u0441)/iu.test(
@@ -777,6 +856,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
const followupContext = toRecordObject(input.followupContext);
const predecomposeEntities = collectPredecomposeEntities(predecomposeContract);
const followupSeed = collectFollowupDiscoverySeed(followupContext);
const metadataSurfaceRef = buildMetadataSurfaceRef(followupSeed);
const reasonCodes: string[] = [];
const rawUserText = toNonEmptyString(input.userMessage);
const rawEffectiveText = toNonEmptyString(input.effectiveMessage);
@@ -793,6 +873,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
!rawLifecycleSignal && !rawValueFlowSignal && !rawMetadataSignal && hasEntityResolutionSignal(rawText);
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
const rawAllTimeScopeSignal = hasAllTimeScopeHint(rawText);
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
const rawDateScope = collectDateScopeFromRawText(rawText);
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
@@ -1168,7 +1249,17 @@ export function buildAssistantMcpDiscoveryTurnInput(
}
}
}
const explicitDateScope = assistantTurnMeaningDateScope ?? predecomposeDateScope ?? rawDateScope ?? followupSeed.dateScope;
const explicitDateScope =
rawAllTimeScopeSignal
? null
: assistantTurnMeaningDateScope ?? predecomposeDateScope ?? rawDateScope ?? followupSeed.dateScope;
const followupDateScopeApplied = Boolean(
!rawAllTimeScopeSignal &&
!assistantTurnMeaningDateScope &&
!predecomposeDateScope &&
!rawDateScope &&
followupSeed.dateScope
);
const turnMeaning: AssistantMcpDiscoveryTurnMeaningRef = {
asked_domain_family:
@@ -1363,6 +1454,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
if (monthlyAggregationSignal) {
pushReason(reasonCodes, "mcp_discovery_monthly_aggregation_signal_detected");
}
if (rawAllTimeScopeSignal) {
pushReason(reasonCodes, "mcp_discovery_all_time_scope_signal_detected");
}
if (followupDiscoverySeedApplicable) {
pushReason(reasonCodes, "mcp_discovery_seeded_from_followup_context");
}
@@ -1432,9 +1526,15 @@ export function buildAssistantMcpDiscoveryTurnInput(
if (followupSeed.counterparty) {
pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context");
}
if (followupSeed.dateScope) {
if (followupDateScopeApplied) {
pushReason(reasonCodes, "mcp_discovery_date_scope_from_followup_context");
}
if (metadataSurfaceRef) {
pushReason(reasonCodes, "mcp_discovery_metadata_surface_ref_from_followup_context");
}
if (metadataSurfaceRef?.recommended_next_primitive) {
pushReason(reasonCodes, "mcp_discovery_metadata_next_primitive_from_followup_context");
}
if (entityCandidates.length > 0) {
pushReason(reasonCodes, "mcp_discovery_entity_scope_available");
}
@@ -1463,6 +1563,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
should_run_discovery: runDiscovery,
semantic_data_need: runDiscovery ? semanticDataNeed : null,
data_need_graph: dataNeedGraph,
metadata_surface_ref: runDiscovery ? metadataSurfaceRef : null,
turn_meaning_ref: runDiscovery && hasTurnMeaning ? cleanTurnMeaning : null,
source_signal: sourceSignal,
reason_codes: reasonCodes
@@ -16,7 +16,10 @@ import {
readAssistantMcpDiscoveryEntityAmbiguityCandidates,
readAssistantMcpDiscoveryEntityResolutionStatus,
readAssistantMcpDiscoveryMetadataRouteFamily,
readAssistantMcpDiscoveryMetadataRouteFamilySelectionBasis,
readAssistantMcpDiscoveryMetadataSelectedEntitySet,
readAssistantMcpDiscoveryMetadataSelectedSurfaceObjects,
readAssistantMcpDiscoveryMetadataRecommendedNextPrimitive,
readAssistantMcpDiscoveryRankingNeed,
readAddressDebugTemporalScope,
readAssistantMcpDiscoveryPilotScope,
@@ -665,10 +668,18 @@ export function createAssistantTransitionPolicy(deps) {
carryoverSourceDebug,
deps.toNonEmptyString
);
const sourceDiscoveryMetadataRouteFamilySelectionBasis =
readAssistantMcpDiscoveryMetadataRouteFamilySelectionBasis(carryoverSourceDebug, deps.toNonEmptyString);
const sourceDiscoveryMetadataSelectedEntitySet = readAssistantMcpDiscoveryMetadataSelectedEntitySet(
carryoverSourceDebug,
deps.toNonEmptyString
);
const sourceDiscoveryMetadataSelectedSurfaceObjects = readAssistantMcpDiscoveryMetadataSelectedSurfaceObjects(
carryoverSourceDebug,
deps.toNonEmptyString
);
const sourceDiscoveryMetadataRecommendedNextPrimitive =
readAssistantMcpDiscoveryMetadataRecommendedNextPrimitive(carryoverSourceDebug, deps.toNonEmptyString);
const sourceDiscoveryMetadataAmbiguityDetected = readAssistantMcpDiscoveryMetadataAmbiguityDetected(
carryoverSourceDebug
);
@@ -1037,7 +1048,15 @@ export function createAssistantTransitionPolicy(deps) {
? sourceDiscoveryEntityAmbiguityCandidates
: undefined,
previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined,
previous_discovery_metadata_route_family_selection_basis:
sourceDiscoveryMetadataRouteFamilySelectionBasis ?? undefined,
previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined,
previous_discovery_metadata_selected_surface_objects:
sourceDiscoveryMetadataSelectedSurfaceObjects.length > 0
? sourceDiscoveryMetadataSelectedSurfaceObjects
: undefined,
previous_discovery_metadata_recommended_next_primitive:
sourceDiscoveryMetadataRecommendedNextPrimitive ?? undefined,
previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined,
previous_discovery_metadata_ambiguity_entity_sets:
sourceDiscoveryMetadataAmbiguityEntitySets.length > 0 ? sourceDiscoveryMetadataAmbiguityEntitySets : undefined,