ARCH: добавить MCP catalog index для discovery

This commit is contained in:
2026-04-20 09:06:40 +03:00
parent a75da40178
commit 1752d1babf
4 changed files with 575 additions and 0 deletions
@@ -0,0 +1,203 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION = exports.ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION = void 0;
exports.buildAssistantMcpCatalogIndex = buildAssistantMcpCatalogIndex;
exports.getAssistantMcpCatalogPrimitive = getAssistantMcpCatalogPrimitive;
exports.reviewAssistantMcpDiscoveryPlanAgainstCatalog = reviewAssistantMcpDiscoveryPlanAgainstCatalog;
const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
exports.ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION = "assistant_mcp_catalog_index_v1";
exports.ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION = "assistant_mcp_catalog_plan_review_v1";
const PRIMITIVE_CONTRACTS = [
{
primitive_id: "search_business_entity",
purpose: "Find candidate 1C business entities by user wording before a fact query is executed.",
required_axes_any_of: [["business_entity"], ["counterparty"], ["organization"], ["contract"], ["item"]],
optional_axes: ["period", "document", "account"],
output_fact_kinds: ["entity_candidates", "entity_ambiguity"],
evidence_floor: "rows_received",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "inspect_1c_metadata",
purpose: "Inspect available 1C schema/catalog/document/register surface before selecting a query lane.",
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"],
evidence_floor: "source_summary",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "resolve_entity_reference",
purpose: "Resolve a user-visible entity name to a concrete 1C reference candidate.",
required_axes_any_of: [["business_entity"], ["counterparty"], ["organization"], ["contract"], ["item"]],
optional_axes: ["period", "inn", "document"],
output_fact_kinds: ["resolved_entity_ref", "entity_conflict"],
evidence_floor: "rows_matched",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "query_movements",
purpose: "Fetch or aggregate accounting/register movements for a scoped business question.",
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"],
evidence_floor: "rows_matched",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "query_documents",
purpose: "Fetch documents related to a scoped entity, period, contract, or movement explanation.",
required_axes_any_of: [["document"], ["counterparty"], ["contract"], ["period", "organization"]],
optional_axes: ["account", "amount", "item", "warehouse"],
output_fact_kinds: ["document_rows", "document_dates", "document_amounts"],
evidence_floor: "rows_matched",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "aggregate_by_axis",
purpose: "Aggregate already-scoped 1C evidence by a business axis such as counterparty, contract, or period.",
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"],
evidence_floor: "rows_matched",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "drilldown_related_objects",
purpose: "Drill from a known entity or document into related contracts, documents, movements, or payments.",
required_axes_any_of: [["business_entity"], ["document"], ["contract"], ["counterparty"]],
optional_axes: ["period", "account", "amount"],
output_fact_kinds: ["related_objects", "relationship_edges"],
evidence_floor: "rows_received",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "probe_coverage",
purpose: "Check whether the selected MCP/schema route can prove the requested fact or only support a bounded inference.",
required_axes_any_of: [["coverage_target"], ["domain_family"], ["primitive_id"]],
optional_axes: ["period", "organization", "counterparty", "document", "account"],
output_fact_kinds: ["coverage_status", "known_gaps"],
evidence_floor: "source_summary",
safe_for_model_planning: true,
runtime_must_execute: true
},
{
primitive_id: "explain_evidence_basis",
purpose: "Produce a machine-readable explanation of which checked MCP evidence supports, limits, or fails the answer.",
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"],
evidence_floor: "source_summary",
safe_for_model_planning: true,
runtime_must_execute: true
}
];
const PRIMITIVE_CONTRACT_MAP = new Map(PRIMITIVE_CONTRACTS.map((contract) => [contract.primitive_id, contract]));
function toStringSet(values) {
return new Set(values.map((item) => item.trim()).filter((item) => item.length > 0));
}
function normalizeReasonCode(value) {
const normalized = value
.trim()
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
.replace(/^_+|_+$/g, "")
.toLowerCase();
return normalized.length > 0 ? normalized.slice(0, 120) : null;
}
function pushReason(target, value) {
const normalized = normalizeReasonCode(value);
if (normalized && !target.includes(normalized)) {
target.push(normalized);
}
}
function hasAnyAxisGroup(axisSet, groups) {
return groups.some((group) => group.every((axis) => axisSet.has(axis)));
}
function missingAxisGroups(axisSet, groups) {
return groups.filter((group) => !group.every((axis) => axisSet.has(axis)));
}
function buildAssistantMcpCatalogIndex() {
const reasonCodes = [];
const missingContracts = assistantMcpDiscoveryPolicy_1.ASSISTANT_MCP_DISCOVERY_PRIMITIVES.filter((primitive) => !PRIMITIVE_CONTRACT_MAP.has(primitive));
if (missingContracts.length > 0) {
pushReason(reasonCodes, "catalog_missing_discovery_primitive_contract");
}
else {
pushReason(reasonCodes, "catalog_covers_all_discovery_primitives");
}
return {
schema_version: exports.ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION,
policy_owner: "assistantMcpCatalogIndex",
primitives: PRIMITIVE_CONTRACTS,
reason_codes: reasonCodes
};
}
function getAssistantMcpCatalogPrimitive(primitive) {
const contract = PRIMITIVE_CONTRACT_MAP.get(primitive);
if (!contract) {
throw new Error(`Missing MCP catalog primitive contract: ${primitive}`);
}
return contract;
}
function reviewAssistantMcpDiscoveryPlanAgainstCatalog(plan) {
const reasonCodes = [];
const axisSet = toStringSet(plan.required_axes);
const reviewedPrimitives = [];
const missingAxesByPrimitive = {};
const unknownPrimitives = [];
const evidenceFloors = {};
for (const primitive of plan.allowed_primitives) {
const contract = PRIMITIVE_CONTRACT_MAP.get(primitive);
if (!contract) {
unknownPrimitives.push(primitive);
continue;
}
reviewedPrimitives.push(primitive);
evidenceFloors[primitive] = contract.evidence_floor;
if (!hasAnyAxisGroup(axisSet, contract.required_axes_any_of)) {
missingAxesByPrimitive[primitive] = missingAxisGroups(axisSet, contract.required_axes_any_of);
}
}
if (unknownPrimitives.length > 0) {
pushReason(reasonCodes, "catalog_unknown_primitive_in_discovery_plan");
}
if (Object.keys(missingAxesByPrimitive).length > 0) {
pushReason(reasonCodes, "catalog_required_axes_missing_for_primitive");
}
if (plan.plan_status !== "allowed") {
pushReason(reasonCodes, "catalog_review_received_non_allowed_plan");
}
let reviewStatus = "catalog_compatible";
if (unknownPrimitives.length > 0 || plan.plan_status === "blocked") {
reviewStatus = "catalog_blocked";
}
else if (plan.plan_status !== "allowed" || Object.keys(missingAxesByPrimitive).length > 0) {
reviewStatus = "needs_more_axes";
}
if (reviewStatus === "catalog_compatible") {
pushReason(reasonCodes, "catalog_plan_compatible");
}
else if (reviewStatus === "catalog_blocked") {
pushReason(reasonCodes, "catalog_plan_blocked");
}
else {
pushReason(reasonCodes, "catalog_plan_needs_more_axes");
}
return {
schema_version: exports.ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION,
policy_owner: "assistantMcpCatalogIndex",
review_status: reviewStatus,
reviewed_primitives: reviewedPrimitives,
missing_axes_by_primitive: missingAxesByPrimitive,
unknown_primitives: unknownPrimitives,
evidence_floors: evidenceFloors,
reason_codes: reasonCodes
};
}