1436 lines
55 KiB
TypeScript
1436 lines
55 KiB
TypeScript
import {
|
|
buildAssistantMcpDiscoveryPlan,
|
|
type AssistantMcpDiscoveryPlanContract,
|
|
type AssistantMcpDiscoveryPrimitive,
|
|
type AssistantMcpDiscoveryTurnMeaningRef
|
|
} from "./assistantMcpDiscoveryPolicy";
|
|
import {
|
|
getAssistantMcpCatalogChainTemplate,
|
|
searchAssistantMcpCatalogChainTemplatesByFactAxis,
|
|
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
|
|
searchAssistantMcpCatalogPrimitivesByFactAxis,
|
|
searchAssistantMcpCatalogPrimitivesByMetadataSurface,
|
|
reviewAssistantMcpDiscoveryPlanAgainstCatalog,
|
|
type AssistantMcpCatalogChainTemplateId,
|
|
type AssistantMcpCatalogPlanReview
|
|
} from "./assistantMcpCatalogIndex";
|
|
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
|
|
|
|
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 interface AssistantMcpDiscoveryCatalogChainTemplateAlignment {
|
|
alignment_status: AssistantMcpDiscoveryCatalogChainTemplateAlignmentStatus;
|
|
top_chain_template_match: AssistantMcpCatalogChainTemplateId | null;
|
|
selected_chain_template_rank: number | null;
|
|
selected_chain_is_catalog_template: boolean;
|
|
selected_chain_in_catalog_matches: boolean;
|
|
selected_chain_matches_top: boolean;
|
|
}
|
|
|
|
export type AssistantMcpDiscoveryCatalogChainTemplateAlignmentStatus =
|
|
| "selected_matches_top"
|
|
| "selected_lower_rank"
|
|
| "selected_outside_match_set"
|
|
| "selected_unscored"
|
|
| "not_catalog_template";
|
|
|
|
export type AssistantMcpDiscoveryChainId =
|
|
| "metadata_inspection"
|
|
| "catalog_drilldown"
|
|
| "metadata_lane_clarification"
|
|
| "inventory_stock_snapshot"
|
|
| "inventory_supplier_overlap"
|
|
| "inventory_purchase_provenance"
|
|
| "inventory_sale_trace"
|
|
| "value_flow"
|
|
| "value_flow_comparison"
|
|
| "value_flow_ranking"
|
|
| "lifecycle"
|
|
| "business_overview"
|
|
| "movement_evidence"
|
|
| "document_evidence"
|
|
| "entity_resolution";
|
|
|
|
export interface AssistantMcpDiscoveryPlannerInput {
|
|
semanticDataNeed?: string | null;
|
|
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
|
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
|
turnMeaning?: AssistantMcpDiscoveryTurnMeaningRef | null;
|
|
}
|
|
|
|
export interface AssistantMcpDiscoveryPlannerContract {
|
|
schema_version: typeof ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION;
|
|
policy_owner: "assistantMcpDiscoveryPlanner";
|
|
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;
|
|
catalog_chain_template_matches: AssistantMcpCatalogChainTemplateId[];
|
|
catalog_chain_template_alignment: AssistantMcpDiscoveryCatalogChainTemplateAlignment;
|
|
proposed_primitives: AssistantMcpDiscoveryPrimitive[];
|
|
required_axes: string[];
|
|
discovery_plan: AssistantMcpDiscoveryPlanContract;
|
|
catalog_review: AssistantMcpCatalogPlanReview;
|
|
reason_codes: string[];
|
|
}
|
|
|
|
interface PlannerRecipe {
|
|
semanticDataNeed: string;
|
|
chainId: AssistantMcpDiscoveryChainId;
|
|
chainSummary: string;
|
|
primitives: AssistantMcpDiscoveryPrimitive[];
|
|
axes: string[];
|
|
reason: string;
|
|
extraReasons?: string[];
|
|
}
|
|
|
|
interface PlannerBudgetOverride {
|
|
maxProbeCount?: number;
|
|
}
|
|
|
|
function toNonEmptyString(value: unknown): string | null {
|
|
if (value === null || value === undefined) {
|
|
return null;
|
|
}
|
|
const text = String(value).trim();
|
|
return text.length > 0 ? text : null;
|
|
}
|
|
|
|
function lower(value: unknown): string {
|
|
return String(value ?? "").trim().toLowerCase();
|
|
}
|
|
|
|
function normalizeReasonCode(value: string): string | null {
|
|
const normalized = value
|
|
.trim()
|
|
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
|
.replace(/^_+|_+$/g, "")
|
|
.toLowerCase();
|
|
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
|
}
|
|
|
|
function pushReason(target: string[], value: string): void {
|
|
const normalized = normalizeReasonCode(value);
|
|
if (normalized && !target.includes(normalized)) {
|
|
target.push(normalized);
|
|
}
|
|
}
|
|
|
|
function pushUnique(target: string[], value: string): void {
|
|
const text = value.trim();
|
|
if (text && !target.includes(text)) {
|
|
target.push(text);
|
|
}
|
|
}
|
|
|
|
function pushAllUnique(target: string[], values: string[]): void {
|
|
for (const value of values) {
|
|
pushUnique(target, value);
|
|
}
|
|
}
|
|
|
|
function pushCatalogChainTemplateAlignmentReasons(
|
|
target: string[],
|
|
alignment: AssistantMcpDiscoveryCatalogChainTemplateAlignment
|
|
): void {
|
|
if (alignment.alignment_status === "selected_matches_top") {
|
|
pushReason(target, "planner_catalog_chain_template_alignment_evaluated");
|
|
pushReason(target, "planner_selected_chain_matches_catalog_top");
|
|
return;
|
|
}
|
|
if (alignment.alignment_status === "selected_lower_rank") {
|
|
pushReason(target, "planner_catalog_chain_template_alignment_evaluated");
|
|
pushReason(target, "planner_selected_chain_uses_lower_rank_catalog_match");
|
|
return;
|
|
}
|
|
if (alignment.alignment_status === "selected_outside_match_set") {
|
|
pushReason(target, "planner_catalog_chain_template_alignment_evaluated");
|
|
pushReason(target, "planner_selected_chain_outside_catalog_match_set");
|
|
return;
|
|
}
|
|
if (alignment.alignment_status === "selected_unscored") {
|
|
pushReason(target, "planner_catalog_chain_template_alignment_unscored");
|
|
}
|
|
}
|
|
|
|
const LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES = [
|
|
"planner_lifecycle_bounded_activity_window_template",
|
|
"planner_lifecycle_legal_fact_boundary_required"
|
|
];
|
|
|
|
function recipeFromCatalogChainTemplate(input: {
|
|
chainId: AssistantMcpCatalogChainTemplateId;
|
|
axes: string[];
|
|
primitives?: AssistantMcpDiscoveryPrimitive[];
|
|
reason: string;
|
|
extraReasons?: string[];
|
|
chainSummary?: string;
|
|
semanticDataNeed?: string;
|
|
}): PlannerRecipe {
|
|
const template = getAssistantMcpCatalogChainTemplate(input.chainId);
|
|
const axes = [...input.axes];
|
|
pushAllUnique(axes, template.base_required_axes);
|
|
return {
|
|
semanticDataNeed: input.semanticDataNeed ?? template.semantic_data_need,
|
|
chainId: template.chain_id,
|
|
chainSummary: input.chainSummary ?? template.chain_summary,
|
|
primitives: input.primitives ?? template.fallback_primitives,
|
|
axes,
|
|
reason: input.reason,
|
|
extraReasons: [
|
|
`planner_instantiated_catalog_chain_template_${template.chain_id}`,
|
|
...(input.extraReasons ?? [])
|
|
]
|
|
};
|
|
}
|
|
|
|
function hasEntity(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): boolean {
|
|
return (meaning?.explicit_entity_candidates?.length ?? 0) > 0;
|
|
}
|
|
|
|
function hasSubjectCandidates(graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined): boolean {
|
|
return (graph?.subject_candidates.length ?? 0) > 0;
|
|
}
|
|
|
|
function hasMetadataScopedOpenLane(
|
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined,
|
|
meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined
|
|
): boolean {
|
|
return Boolean(
|
|
graph?.subject_resolution_optional === true ||
|
|
meaning?.subject_resolution_optional === true
|
|
);
|
|
}
|
|
|
|
function hasReasonCode(
|
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined,
|
|
reasonCode: string
|
|
): boolean {
|
|
return (graph?.reason_codes ?? []).includes(reasonCode);
|
|
}
|
|
|
|
function aggregationAxis(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): string | null {
|
|
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
|
}
|
|
|
|
function addScopeAxes(axes: string[], meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): void {
|
|
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: string[], meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): void {
|
|
if (toNonEmptyString(meaning?.metadata_scope_hint)) {
|
|
pushUnique(axes, "metadata_scope");
|
|
}
|
|
}
|
|
|
|
function addTimeScopeAxes(
|
|
axes: string[],
|
|
dataNeedGraph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined
|
|
): void {
|
|
if (dataNeedGraph?.time_scope_need === "all_time_scope") {
|
|
pushUnique(axes, "all_time_scope");
|
|
}
|
|
}
|
|
|
|
function includesAny(text: string, tokens: string[]): boolean {
|
|
return tokens.some((token) => text.includes(token));
|
|
}
|
|
|
|
function isYearDateScope(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): boolean {
|
|
return /^\d{4}$/.test(toNonEmptyString(meaning?.explicit_date_scope) ?? "");
|
|
}
|
|
|
|
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 normalizeMetadataSurfaceToken(value: unknown): string {
|
|
return String(value ?? "").trim().toLowerCase().replace(/[\s_.-]+/g, "");
|
|
}
|
|
|
|
function metadataSurfaceValueSuggestsDocument(value: unknown): boolean {
|
|
const token = normalizeMetadataSurfaceToken(value);
|
|
return token.includes("document") || token.includes("invoice") || token.includes("waybill") || token.includes("act");
|
|
}
|
|
|
|
function metadataSurfaceValueSuggestsMovement(value: unknown): boolean {
|
|
const token = normalizeMetadataSurfaceToken(value);
|
|
return token.includes("register") || token.includes("movement") || token.includes("operation") || token.includes("bank");
|
|
}
|
|
|
|
function metadataSurfaceValueSuggestsCatalog(value: unknown): boolean {
|
|
const token = normalizeMetadataSurfaceToken(value);
|
|
return token.includes("catalog") || token.includes("directory");
|
|
}
|
|
|
|
function routeFamilyFromMetadataSurfaceRef(
|
|
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
|
): AssistantMcpDiscoveryMetadataRouteFamily | null {
|
|
if (!surface || surface.ambiguity_detected) {
|
|
return null;
|
|
}
|
|
if (surface.downstream_route_family) {
|
|
return surface.downstream_route_family;
|
|
}
|
|
|
|
const values = [surface.selected_entity_set ?? "", ...surface.selected_surface_objects];
|
|
const documentHit = values.some(metadataSurfaceValueSuggestsDocument);
|
|
const movementHit = values.some(metadataSurfaceValueSuggestsMovement);
|
|
const catalogHit = values.some(metadataSurfaceValueSuggestsCatalog);
|
|
const hitCount = [documentHit, movementHit, catalogHit].filter(Boolean).length;
|
|
if (hitCount !== 1) {
|
|
return null;
|
|
}
|
|
if (documentHit) {
|
|
return "document_evidence";
|
|
}
|
|
if (movementHit) {
|
|
return "movement_evidence";
|
|
}
|
|
return "catalog_drilldown";
|
|
}
|
|
|
|
function inferredRouteFamilyFromMetadataSurfaceRef(
|
|
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
|
): boolean {
|
|
return Boolean(surface && !surface.downstream_route_family && routeFamilyFromMetadataSurfaceRef(surface));
|
|
}
|
|
|
|
function preferredPrimitiveForRouteFamily(
|
|
routeFamily: AssistantMcpDiscoveryMetadataRouteFamily | null
|
|
): AssistantMcpDiscoveryPrimitive | null {
|
|
if (routeFamily === "document_evidence") {
|
|
return "query_documents";
|
|
}
|
|
if (routeFamily === "movement_evidence") {
|
|
return "query_movements";
|
|
}
|
|
if (routeFamily === "catalog_drilldown") {
|
|
return "drilldown_related_objects";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function preferredPrimitiveFromMetadataSurface(
|
|
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
|
): AssistantMcpDiscoveryPrimitive | null {
|
|
if (surface?.ambiguity_detected) {
|
|
return null;
|
|
}
|
|
const recommendedPrimitive = surface?.recommended_next_primitive ?? null;
|
|
if (recommendedPrimitive) {
|
|
return recommendedPrimitive;
|
|
}
|
|
return preferredPrimitiveForRouteFamily(routeFamilyFromMetadataSurfaceRef(surface));
|
|
}
|
|
|
|
function preferredPrimitiveFromExplicitDataNeedGraph(
|
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined
|
|
): AssistantMcpDiscoveryPrimitive | null {
|
|
const factFamily = lower(graph?.business_fact_family);
|
|
const actionFamily = lower(graph?.action_family);
|
|
if (factFamily === "document_evidence" || actionFamily === "list_documents") {
|
|
return "query_documents";
|
|
}
|
|
if (factFamily === "movement_evidence" || actionFamily === "list_movements") {
|
|
return "query_movements";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function catalogTemplateIdFromChainId(
|
|
chainId: AssistantMcpDiscoveryChainId
|
|
): AssistantMcpCatalogChainTemplateId | null {
|
|
if (chainId === "metadata_lane_clarification") {
|
|
return null;
|
|
}
|
|
return chainId;
|
|
}
|
|
|
|
function promoteConfirmedMetadataSurfaceChainTemplate(input: {
|
|
matches: AssistantMcpCatalogChainTemplateId[];
|
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
|
selectedChainId?: AssistantMcpCatalogChainTemplateId | null;
|
|
}): AssistantMcpCatalogChainTemplateId[] {
|
|
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(input.metadataSurface);
|
|
const selectedChainId = input.selectedChainId ?? null;
|
|
if (!surfaceRouteFamily || !selectedChainId || selectedChainId !== surfaceRouteFamily) {
|
|
return input.matches;
|
|
}
|
|
if (input.matches[0] === selectedChainId || !input.matches.includes(selectedChainId)) {
|
|
return input.matches;
|
|
}
|
|
return [
|
|
selectedChainId,
|
|
...input.matches.filter((chainId) => chainId !== selectedChainId)
|
|
];
|
|
}
|
|
|
|
function hasCarriedMetadataSurfaceScoringEvidence(
|
|
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
|
): boolean {
|
|
return Boolean(
|
|
surface &&
|
|
(toNonEmptyString(surface.selected_entity_set) ||
|
|
surface.selected_surface_objects.length > 0 ||
|
|
surface.ambiguity_entity_sets.length > 0)
|
|
);
|
|
}
|
|
|
|
function filterCatalogPrimitivesByMetadataSurface(input: {
|
|
catalogPrimitives: AssistantMcpDiscoveryPrimitive[];
|
|
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[];
|
|
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
|
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
|
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
|
|
const surfacePreferredPrimitive = preferredPrimitiveFromMetadataSurface(input.metadataSurface);
|
|
const dataNeedPreferredPrimitive = preferredPrimitiveFromExplicitDataNeedGraph(input.dataNeedGraph);
|
|
const preferredPrimitive = surfacePreferredPrimitive ?? dataNeedPreferredPrimitive;
|
|
const hasSurfaceScoringEvidence = hasCarriedMetadataSurfaceScoringEvidence(input.metadataSurface);
|
|
const reasonCodes: string[] = [];
|
|
if (
|
|
!preferredPrimitive ||
|
|
!input.fallbackPrimitives.includes(preferredPrimitive)
|
|
) {
|
|
return {
|
|
primitives: input.catalogPrimitives,
|
|
reasonCodes
|
|
};
|
|
}
|
|
|
|
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(
|
|
surfacePreferredPrimitive
|
|
? "planner_filtered_catalog_primitives_by_confirmed_metadata_surface"
|
|
: "planner_filtered_catalog_primitives_by_explicit_data_need_lane"
|
|
);
|
|
}
|
|
if (
|
|
(filteredPrimitives.includes(preferredPrimitive) || input.fallbackPrimitives.includes(preferredPrimitive)) &&
|
|
hasSurfaceScoringEvidence
|
|
) {
|
|
reasonCodes.push(
|
|
surfacePreferredPrimitive
|
|
? "planner_surface_aware_next_lane_from_confirmed_metadata_objects"
|
|
: "planner_metadata_surface_scored_with_explicit_lane_family"
|
|
);
|
|
}
|
|
return {
|
|
primitives: filteredPrimitives,
|
|
reasonCodes
|
|
};
|
|
}
|
|
|
|
function selectPrimitivesFromGraphAndCatalog(input: {
|
|
dataNeedGraph: AssistantMcpDiscoveryDataNeedGraphContract | null;
|
|
fallbackPrimitives: AssistantMcpDiscoveryPrimitive[];
|
|
requiredAxes: string[];
|
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
|
actionFamily?: string | null;
|
|
allowAggregateByAxis?: boolean;
|
|
selectedChainId?: AssistantMcpCatalogChainTemplateId | null;
|
|
}): { 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 rawChainTemplateMatches = input.dataNeedGraph
|
|
? searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
|
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
|
|
})
|
|
: [];
|
|
const chainTemplateMatches = promoteConfirmedMetadataSurfaceChainTemplate({
|
|
matches: rawChainTemplateMatches,
|
|
metadataSurface: input.metadataSurface,
|
|
selectedChainId: input.selectedChainId
|
|
});
|
|
if (chainTemplateMatches.length > 0) {
|
|
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
|
if (rawChainTemplateMatches[0] !== chainTemplateMatches[0]) {
|
|
reasonCodes.push("planner_catalog_chain_template_promoted_by_confirmed_metadata_surface");
|
|
}
|
|
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
|
}
|
|
|
|
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,
|
|
dataNeedGraph: input.dataNeedGraph,
|
|
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: AssistantMcpDiscoveryPlannerInput, recipe: PlannerRecipe): PlannerBudgetOverride {
|
|
const meaning = input.turnMeaning ?? null;
|
|
const requestedAggregationAxis = aggregationAxis(meaning);
|
|
const isValueFlowRecipe =
|
|
recipe.primitives.includes("query_movements") &&
|
|
(recipe.semanticDataNeed === "counterparty value-flow evidence" ||
|
|
recipe.semanticDataNeed === "bidirectional value-flow comparison evidence" ||
|
|
recipe.semanticDataNeed === "ranked value-flow evidence");
|
|
if (!isValueFlowRecipe) {
|
|
return {};
|
|
}
|
|
if (requestedAggregationAxis === "month" || isYearDateScope(meaning)) {
|
|
return {
|
|
maxProbeCount: 30
|
|
};
|
|
}
|
|
return {};
|
|
}
|
|
|
|
function catalogChainTemplateMatchesForContract(
|
|
input: AssistantMcpDiscoveryPlannerInput,
|
|
recipe: PlannerRecipe
|
|
): AssistantMcpCatalogChainTemplateId[] {
|
|
const dataNeedGraph = input.dataNeedGraph ?? null;
|
|
if (!dataNeedGraph) {
|
|
return [];
|
|
}
|
|
const matches = searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
|
business_fact_family: dataNeedGraph.business_fact_family,
|
|
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
|
|
required_axes: recipe.axes,
|
|
comparison_need: dataNeedGraph.comparison_need,
|
|
ranking_need: dataNeedGraph.ranking_need,
|
|
aggregation_need: dataNeedGraph.aggregation_need
|
|
});
|
|
return promoteConfirmedMetadataSurfaceChainTemplate({
|
|
matches,
|
|
metadataSurface: input.metadataSurface,
|
|
selectedChainId: catalogTemplateIdFromChainId(recipe.chainId)
|
|
});
|
|
}
|
|
|
|
function catalogChainTemplateAlignmentForContract(
|
|
recipe: PlannerRecipe,
|
|
matches: AssistantMcpCatalogChainTemplateId[]
|
|
): AssistantMcpDiscoveryCatalogChainTemplateAlignment {
|
|
const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification";
|
|
const selectedIndex = matches.indexOf(recipe.chainId as AssistantMcpCatalogChainTemplateId);
|
|
const alignmentStatus: AssistantMcpDiscoveryCatalogChainTemplateAlignmentStatus = !selectedChainIsCatalogTemplate
|
|
? "not_catalog_template"
|
|
: matches.length <= 0
|
|
? "selected_unscored"
|
|
: selectedIndex === 0
|
|
? "selected_matches_top"
|
|
: selectedIndex > 0
|
|
? "selected_lower_rank"
|
|
: "selected_outside_match_set";
|
|
return {
|
|
alignment_status: alignmentStatus,
|
|
top_chain_template_match: matches[0] ?? null,
|
|
selected_chain_template_rank: selectedIndex >= 0 ? selectedIndex + 1 : null,
|
|
selected_chain_is_catalog_template: selectedChainIsCatalogTemplate,
|
|
selected_chain_in_catalog_matches: selectedIndex >= 0,
|
|
selected_chain_matches_top: selectedIndex === 0
|
|
};
|
|
}
|
|
|
|
function routeFamilyFromThinMetadataSurfaceInput(
|
|
input: AssistantMcpDiscoveryPlannerInput
|
|
): AssistantMcpDiscoveryMetadataRouteFamily | null {
|
|
const surface = input.metadataSurface ?? null;
|
|
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(surface);
|
|
if (!surface || surface.ambiguity_detected || !surfaceRouteFamily) {
|
|
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 surfaceRouteFamily === "document_evidence" ? "document_evidence" : null;
|
|
}
|
|
if (graphFactFamily === "movement_evidence" || includesAny(combined, ["movement", "movements", "list_movements", "bank_operations"])) {
|
|
return surfaceRouteFamily === "movement_evidence" ? "movement_evidence" : null;
|
|
}
|
|
if (graphFactFamily === "schema_surface" || includesAny(combined, ["catalog", "directory", "inspect_catalog"])) {
|
|
return surfaceRouteFamily === "catalog_drilldown" ? "catalog_drilldown" : null;
|
|
}
|
|
if (!graphFactFamily && !domain && !action) {
|
|
return surfaceRouteFamily;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
|
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: string[] = [];
|
|
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 template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
selectedChainId: "document_evidence"
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "document_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_document_from_confirmed_metadata_surface_ref",
|
|
chainSummary:
|
|
"Ground the next checked document lane from the confirmed metadata surface, then fetch scoped document rows and probe coverage before answering.",
|
|
extraReasons: [
|
|
...primitiveSelection.reasonCodes,
|
|
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
|
|
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
|
|
: [])
|
|
]
|
|
});
|
|
}
|
|
if (thinSurfaceRouteFamily === "movement_evidence") {
|
|
pushUnique(axes, "coverage_target");
|
|
const template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
selectedChainId: "movement_evidence"
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "movement_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_movement_from_confirmed_metadata_surface_ref",
|
|
chainSummary:
|
|
"Ground the next checked movement lane from the confirmed metadata surface, then fetch scoped movement rows and probe coverage before answering.",
|
|
extraReasons: [
|
|
...primitiveSelection.reasonCodes,
|
|
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
|
|
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
|
|
: [])
|
|
]
|
|
});
|
|
}
|
|
if (thinSurfaceRouteFamily === "catalog_drilldown") {
|
|
pushUnique(axes, "metadata_scope");
|
|
const template = getAssistantMcpCatalogChainTemplate("catalog_drilldown");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
selectedChainId: "catalog_drilldown"
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "catalog_drilldown",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref",
|
|
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.",
|
|
extraReasons: [
|
|
...primitiveSelection.reasonCodes,
|
|
...(inferredRouteFamilyFromMetadataSurfaceRef(input.metadataSurface)
|
|
? ["planner_inferred_next_lane_from_unambiguous_metadata_surface"]
|
|
: [])
|
|
]
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "value_flow") {
|
|
if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing") {
|
|
pushUnique(axes, "amount");
|
|
pushUnique(axes, "coverage_target");
|
|
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
|
|
pushUnique(axes, "calendar_month");
|
|
}
|
|
const template = getAssistantMcpCatalogChainTemplate("value_flow_comparison");
|
|
const fallbackPrimitives = hasSubjectCandidates(dataNeedGraph)
|
|
? (["resolve_entity_reference", ...template.fallback_primitives] as AssistantMcpDiscoveryPrimitive[])
|
|
: template.fallback_primitives;
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
allowAggregateByAxis: false
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "value_flow_comparison",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 template = getAssistantMcpCatalogChainTemplate("value_flow_ranking");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
allowAggregateByAxis: true
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "value_flow_ranking",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 recipeFromCatalogChainTemplate({
|
|
chainId: "value_flow",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
semanticDataNeed: "organization-scoped value-flow evidence",
|
|
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.",
|
|
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 template = getAssistantMcpCatalogChainTemplate("value_flow");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action,
|
|
allowAggregateByAxis: true
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "value_flow",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 === "business_overview") {
|
|
pushUnique(axes, "organization");
|
|
pushUnique(axes, "aggregate_axis");
|
|
pushUnique(axes, "amount");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
pushUnique(axes, "profit_margin_boundary");
|
|
const template = getAssistantMcpCatalogChainTemplate("business_overview");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action || graphAction,
|
|
allowAggregateByAxis: true,
|
|
selectedChainId: "business_overview"
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "business_overview",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_business_overview_from_data_need_graph",
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "activity_lifecycle") {
|
|
pushUnique(axes, "document_date");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
const template = getAssistantMcpCatalogChainTemplate("lifecycle");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "lifecycle",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_lifecycle_from_data_need_graph",
|
|
extraReasons: [...primitiveSelection.reasonCodes, ...LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES]
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "inventory_stock_snapshot") {
|
|
pushUnique(axes, "as_of_date");
|
|
pushUnique(axes, "organization");
|
|
pushUnique(axes, "warehouse");
|
|
pushUnique(axes, "aggregate_axis");
|
|
pushUnique(axes, "quantity");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
const template = getAssistantMcpCatalogChainTemplate("inventory_stock_snapshot");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action || graphAction,
|
|
allowAggregateByAxis: true
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_stock_snapshot",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_inventory_stock_snapshot_from_data_need_graph",
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "inventory_supplier_overlap") {
|
|
pushUnique(axes, "supplier");
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "warehouse");
|
|
pushUnique(axes, "as_of_date");
|
|
pushUnique(axes, "aggregate_axis");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
const template = getAssistantMcpCatalogChainTemplate("inventory_supplier_overlap");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action || graphAction,
|
|
allowAggregateByAxis: true
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_supplier_overlap",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_inventory_supplier_overlap_from_data_need_graph",
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "inventory_purchase_provenance") {
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "document");
|
|
pushUnique(axes, "supplier");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
const template = getAssistantMcpCatalogChainTemplate("inventory_purchase_provenance");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action || graphAction
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_purchase_provenance",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_inventory_purchase_provenance_from_data_need_graph",
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "inventory_sale_trace") {
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "document");
|
|
pushUnique(axes, "buyer");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
const template = getAssistantMcpCatalogChainTemplate("inventory_sale_trace");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action || graphAction
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_sale_trace",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_inventory_sale_trace_from_data_need_graph",
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
|
|
if (graphFactFamily === "schema_surface") {
|
|
pushUnique(axes, "metadata_scope");
|
|
const template = getAssistantMcpCatalogChainTemplate("metadata_inspection");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "metadata_inspection",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: ["query_movements", "probe_coverage"],
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "movement_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_metadata_scoped_movement_from_data_need_graph",
|
|
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.",
|
|
semanticDataNeed: template.semantic_data_need,
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
pushUnique(axes, "coverage_target");
|
|
const template = getAssistantMcpCatalogChainTemplate("movement_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "movement_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: ["query_documents", "probe_coverage"],
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "document_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
reason: "planner_selected_metadata_scoped_document_from_data_need_graph",
|
|
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.",
|
|
semanticDataNeed: template.semantic_data_need,
|
|
extraReasons: primitiveSelection.reasonCodes
|
|
});
|
|
}
|
|
pushUnique(axes, "coverage_target");
|
|
const template = getAssistantMcpCatalogChainTemplate("document_evidence");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "document_evidence",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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 template = getAssistantMcpCatalogChainTemplate("entity_resolution");
|
|
const primitiveSelection = selectPrimitivesFromGraphAndCatalog({
|
|
dataNeedGraph,
|
|
fallbackPrimitives: template.fallback_primitives,
|
|
requiredAxes: axes,
|
|
metadataSurface: input.metadataSurface,
|
|
actionFamily: action
|
|
});
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "entity_resolution",
|
|
axes,
|
|
primitives: primitiveSelection.primitives,
|
|
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, ["inventory_sale_trace", "sale_trace", "sale trace", "purchase_to_sale", "purchase-to-sale", "buyer"])) {
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "document");
|
|
pushUnique(axes, "buyer");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_sale_trace",
|
|
axes,
|
|
reason: "planner_selected_inventory_sale_trace_recipe"
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["inventory_supplier_overlap", "supplier overlap", "supplier stock", "stock by supplier"])) {
|
|
pushUnique(axes, "supplier");
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "warehouse");
|
|
pushUnique(axes, "as_of_date");
|
|
pushUnique(axes, "aggregate_axis");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_supplier_overlap",
|
|
axes,
|
|
reason: "planner_selected_inventory_supplier_overlap_recipe"
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["inventory_purchase_provenance", "purchase_provenance", "purchase provenance", "supplier provenance"])) {
|
|
pushUnique(axes, "item");
|
|
pushUnique(axes, "document");
|
|
pushUnique(axes, "supplier");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_purchase_provenance",
|
|
axes,
|
|
reason: "planner_selected_inventory_purchase_provenance_recipe"
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["inventory", "stock", "warehouse"])) {
|
|
pushUnique(axes, "as_of_date");
|
|
pushUnique(axes, "organization");
|
|
pushUnique(axes, "warehouse");
|
|
pushUnique(axes, "aggregate_axis");
|
|
pushUnique(axes, "quantity");
|
|
pushUnique(axes, "coverage_target");
|
|
pushUnique(axes, "evidence_basis");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "inventory_stock_snapshot",
|
|
axes,
|
|
reason: "planner_selected_inventory_stock_snapshot_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 recipeFromCatalogChainTemplate({
|
|
chainId: "value_flow",
|
|
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 recipeFromCatalogChainTemplate({
|
|
chainId: "lifecycle",
|
|
axes,
|
|
reason: "planner_selected_lifecycle_recipe",
|
|
extraReasons: LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["metadata", "schema", "catalog"])) {
|
|
pushUnique(axes, "metadata_scope");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "metadata_inspection",
|
|
axes,
|
|
reason: "planner_selected_metadata_recipe"
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["movement", "movements", "bank_operations", "movement_evidence", "list_movements"])) {
|
|
pushUnique(axes, "coverage_target");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "movement_evidence",
|
|
axes,
|
|
reason: "planner_selected_movement_recipe"
|
|
});
|
|
}
|
|
|
|
if (includesAny(combined, ["document", "documents"])) {
|
|
pushUnique(axes, "coverage_target");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "document_evidence",
|
|
axes,
|
|
reason: "planner_selected_document_recipe"
|
|
});
|
|
}
|
|
|
|
if (hasEntity(meaning)) {
|
|
pushUnique(axes, "business_entity");
|
|
pushUnique(axes, "coverage_target");
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "entity_resolution",
|
|
axes,
|
|
reason: "planner_selected_entity_resolution_recipe"
|
|
});
|
|
}
|
|
|
|
return recipeFromCatalogChainTemplate({
|
|
chainId: "metadata_inspection",
|
|
semanticDataNeed: "unclassified 1C discovery need",
|
|
chainSummary: "Start with metadata inspection instead of guessing a deeper fact route when the business need is still under-specified.",
|
|
axes,
|
|
reason: "planner_selected_clarification_recipe"
|
|
});
|
|
}
|
|
|
|
function statusFrom(
|
|
plan: AssistantMcpDiscoveryPlanContract,
|
|
review: AssistantMcpCatalogPlanReview
|
|
): AssistantMcpDiscoveryPlannerStatus {
|
|
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";
|
|
}
|
|
|
|
export function planAssistantMcpDiscovery(
|
|
input: AssistantMcpDiscoveryPlannerInput
|
|
): AssistantMcpDiscoveryPlannerContract {
|
|
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 catalogChainTemplateMatches = catalogChainTemplateMatchesForContract(input, recipe);
|
|
const catalogChainTemplateAlignment = catalogChainTemplateAlignmentForContract(recipe, catalogChainTemplateMatches);
|
|
const reasonCodes: string[] = [];
|
|
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");
|
|
}
|
|
pushCatalogChainTemplateAlignmentReasons(reasonCodes, catalogChainTemplateAlignment);
|
|
|
|
const plan = buildAssistantMcpDiscoveryPlan({
|
|
semanticDataNeed,
|
|
turnMeaning: input.turnMeaning,
|
|
proposedPrimitives: recipe.primitives,
|
|
requiredAxes: recipe.axes,
|
|
clarificationGaps: dataNeedGraph?.clarification_gaps ?? [],
|
|
maxProbeCount: budgetOverride.maxProbeCount
|
|
});
|
|
const review = 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" as const,
|
|
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: 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,
|
|
catalog_chain_template_matches: catalogChainTemplateMatches,
|
|
catalog_chain_template_alignment: catalogChainTemplateAlignment,
|
|
proposed_primitives: recipe.primitives,
|
|
required_axes: recipe.axes,
|
|
discovery_plan: plan,
|
|
catalog_review: adjustedReview,
|
|
reason_codes: reasonCodes
|
|
};
|
|
}
|