2277 lines
102 KiB
JavaScript
2277 lines
102 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = void 0;
|
||
exports.executeAssistantMcpDiscoveryPilot = executeAssistantMcpDiscoveryPilot;
|
||
const addressMcpClient_1 = require("./addressMcpClient");
|
||
const assistantMcpDiscoveryRuntimeAdapter_1 = require("./assistantMcpDiscoveryRuntimeAdapter");
|
||
const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
|
||
const addressRecipeCatalog_1 = require("./addressRecipeCatalog");
|
||
exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = "assistant_mcp_discovery_pilot_executor_v1";
|
||
const DEFAULT_DEPS = {
|
||
executeAddressMcpQuery: addressMcpClient_1.executeAddressMcpQuery,
|
||
executeAddressMcpMetadata: addressMcpClient_1.executeAddressMcpMetadata
|
||
};
|
||
const ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT = 1000;
|
||
const ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE = `
|
||
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
|
||
ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Контрагент,
|
||
ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Counterparty,
|
||
Контрагенты.Ссылка КАК КонтрагентСсылка,
|
||
Контрагенты.Ссылка КАК CounterpartyRef,
|
||
Контрагенты.Наименование КАК Наименование
|
||
ИЗ
|
||
Справочник.Контрагенты КАК Контрагенты
|
||
`;
|
||
const ENTITY_RESOLUTION_STOPWORDS = new Set([
|
||
"ооо",
|
||
"ао",
|
||
"зао",
|
||
"ип",
|
||
"llc",
|
||
"ltd",
|
||
"company",
|
||
"контрагент",
|
||
"counterparty",
|
||
"поставщик",
|
||
"supplier",
|
||
"клиент",
|
||
"customer",
|
||
"в",
|
||
"1с",
|
||
"1c",
|
||
"найди",
|
||
"найти",
|
||
"поищи",
|
||
"search",
|
||
"find"
|
||
]);
|
||
function toNonEmptyString(value) {
|
||
if (value === null || value === undefined) {
|
||
return null;
|
||
}
|
||
const text = String(value).trim();
|
||
return text.length > 0 ? text : null;
|
||
}
|
||
function normalizeReasonCode(value) {
|
||
const normalized = value
|
||
.trim()
|
||
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
||
.replace(/^_+|_+$/g, "")
|
||
.toLowerCase();
|
||
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
||
}
|
||
function pushReason(target, value) {
|
||
const normalized = normalizeReasonCode(value);
|
||
if (normalized && !target.includes(normalized)) {
|
||
target.push(normalized);
|
||
}
|
||
}
|
||
function pushUnique(target, value) {
|
||
const text = value.trim();
|
||
if (text && !target.includes(text)) {
|
||
target.push(text);
|
||
}
|
||
}
|
||
function aggregationAxisForPlanner(planner) {
|
||
const axis = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.asked_aggregation_axis)?.toLowerCase();
|
||
return axis === "month" ? "month" : null;
|
||
}
|
||
function firstEntityCandidate(planner) {
|
||
const candidates = planner.discovery_plan.turn_meaning_ref?.explicit_entity_candidates ?? [];
|
||
for (const candidate of candidates) {
|
||
const text = toNonEmptyString(candidate);
|
||
if (text) {
|
||
return text;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function dateScopeToFilters(dateScope) {
|
||
if (!dateScope) {
|
||
return {};
|
||
}
|
||
const yearMatch = dateScope.match(/^(\d{4})$/);
|
||
if (yearMatch) {
|
||
return {
|
||
period_from: `${yearMatch[1]}-01-01`,
|
||
period_to: `${yearMatch[1]}-12-31`
|
||
};
|
||
}
|
||
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||
if (dateMatch) {
|
||
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
||
return {
|
||
period_from: date,
|
||
period_to: date
|
||
};
|
||
}
|
||
return {};
|
||
}
|
||
function buildLifecycleFilters(planner) {
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const counterparty = firstEntityCandidate(planner);
|
||
const organization = toNonEmptyString(meaning?.explicit_organization_scope);
|
||
const dateScope = toNonEmptyString(meaning?.explicit_date_scope);
|
||
return {
|
||
...dateScopeToFilters(dateScope),
|
||
...(counterparty ? { counterparty } : {}),
|
||
...(organization ? { organization } : {}),
|
||
limit: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||
sort: "period_asc"
|
||
};
|
||
}
|
||
function buildValueFlowFilters(planner) {
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const counterparty = firstEntityCandidate(planner);
|
||
const organization = toNonEmptyString(meaning?.explicit_organization_scope);
|
||
const dateScope = toNonEmptyString(meaning?.explicit_date_scope);
|
||
return {
|
||
...dateScopeToFilters(dateScope),
|
||
...(counterparty ? { counterparty } : {}),
|
||
...(organization ? { organization } : {}),
|
||
limit: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||
sort: "period_asc"
|
||
};
|
||
}
|
||
function normalizeEntityResolutionText(value) {
|
||
return String(value ?? "")
|
||
.toLowerCase()
|
||
.replace(/ё/g, "е")
|
||
.replace(/[«»"'`]/g, " ")
|
||
.replace(/[^\p{L}\p{N}\s-]+/gu, " ")
|
||
.replace(/\s+/g, " ")
|
||
.trim();
|
||
}
|
||
function tokenizeEntityResolutionText(value) {
|
||
return normalizeEntityResolutionText(value)
|
||
.split(" ")
|
||
.map((token) => token.trim())
|
||
.filter((token) => token.length >= 2 && !ENTITY_RESOLUTION_STOPWORDS.has(token));
|
||
}
|
||
function isLowQualityEntityResolutionAnchor(value) {
|
||
return tokenizeEntityResolutionText(value).length <= 0;
|
||
}
|
||
function entityResolutionCandidateName(row) {
|
||
const candidates = [
|
||
row["Контрагент"],
|
||
row["Counterparty"],
|
||
row["Наименование"],
|
||
row["name"],
|
||
row["Name"],
|
||
row["registrator"],
|
||
row["Registrator"]
|
||
];
|
||
for (const candidate of candidates) {
|
||
const text = toNonEmptyString(candidate);
|
||
if (text) {
|
||
return text;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function entityResolutionCandidateRef(row) {
|
||
const candidates = [row["КонтрагентСсылка"], row["CounterpartyRef"], row["ref"], row["Ref"]];
|
||
for (const candidate of candidates) {
|
||
const text = toNonEmptyString(candidate);
|
||
if (text) {
|
||
return text;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function scoreEntityResolutionCandidate(name, requested) {
|
||
const normalizedName = normalizeEntityResolutionText(name);
|
||
const normalizedRequested = normalizeEntityResolutionText(requested);
|
||
const requestedTokens = tokenizeEntityResolutionText(requested);
|
||
if (!normalizedName || !normalizedRequested || requestedTokens.length <= 0) {
|
||
return null;
|
||
}
|
||
let score = 0;
|
||
if (normalizedName === normalizedRequested) {
|
||
score += 10_000;
|
||
}
|
||
else if (normalizedName.includes(normalizedRequested)) {
|
||
score += 5_000;
|
||
}
|
||
else if (normalizedRequested.includes(normalizedName) && normalizedName.length >= 4) {
|
||
score += 2_000;
|
||
}
|
||
for (const token of requestedTokens) {
|
||
if (!normalizedName.includes(token)) {
|
||
return null;
|
||
}
|
||
score += Math.max(40, token.length * 20);
|
||
}
|
||
score -= Math.abs(normalizedName.length - normalizedRequested.length);
|
||
return score;
|
||
}
|
||
function deriveEntityResolution(result, requestedEntity) {
|
||
if (!result || result.error || !requestedEntity) {
|
||
return null;
|
||
}
|
||
const checkedCandidates = uniqueCandidateStrings(result.raw_rows
|
||
.map((row) => entityResolutionCandidateName(row))
|
||
.filter((value) => Boolean(value)));
|
||
const scoredCandidates = checkedCandidates
|
||
.map((name) => {
|
||
const score = scoreEntityResolutionCandidate(name, requestedEntity);
|
||
return score === null ? null : { name, score };
|
||
})
|
||
.filter((value) => Boolean(value))
|
||
.sort((left, right) => right.score - left.score || left.name.length - right.name.length || left.name.localeCompare(right.name, "ru"));
|
||
if (scoredCandidates.length <= 0) {
|
||
return {
|
||
requested_entity: requestedEntity,
|
||
resolution_status: "not_found",
|
||
resolved_entity: null,
|
||
resolved_reference: null,
|
||
matched_rows: result.rows.length,
|
||
checked_candidates: checkedCandidates.slice(0, 12),
|
||
ambiguity_candidates: [],
|
||
confidence: null,
|
||
inference_basis: "catalog_counterparty_search_rows"
|
||
};
|
||
}
|
||
const bestCandidate = scoredCandidates[0];
|
||
const bestNormalized = normalizeEntityResolutionText(bestCandidate.name);
|
||
const requestedNormalized = normalizeEntityResolutionText(requestedEntity);
|
||
const requestedTokens = tokenizeEntityResolutionText(requestedEntity);
|
||
const exactMatch = bestNormalized === requestedNormalized;
|
||
const strongContains = requestedTokens.length > 1 && bestNormalized.includes(requestedNormalized);
|
||
const topCandidates = scoredCandidates.filter((candidate) => candidate.score === bestCandidate.score);
|
||
if (topCandidates.length > 1 && !exactMatch && !strongContains) {
|
||
return {
|
||
requested_entity: requestedEntity,
|
||
resolution_status: "ambiguous",
|
||
resolved_entity: null,
|
||
resolved_reference: null,
|
||
matched_rows: result.rows.length,
|
||
checked_candidates: checkedCandidates.slice(0, 12),
|
||
ambiguity_candidates: topCandidates.map((candidate) => candidate.name).slice(0, 6),
|
||
confidence: "low",
|
||
inference_basis: "catalog_counterparty_search_rows"
|
||
};
|
||
}
|
||
const matchedRow = result.raw_rows.find((row) => normalizeEntityResolutionText(entityResolutionCandidateName(row)) === bestNormalized) ?? null;
|
||
return {
|
||
requested_entity: requestedEntity,
|
||
resolution_status: "resolved",
|
||
resolved_entity: bestCandidate.name,
|
||
resolved_reference: matchedRow ? entityResolutionCandidateRef(matchedRow) : null,
|
||
matched_rows: result.rows.length,
|
||
checked_candidates: checkedCandidates.slice(0, 12),
|
||
ambiguity_candidates: [],
|
||
confidence: exactMatch ? "high" : strongContains ? "medium" : "low",
|
||
inference_basis: "catalog_counterparty_search_rows"
|
||
};
|
||
}
|
||
function uniqueCandidateStrings(values) {
|
||
const result = [];
|
||
for (const value of values) {
|
||
pushUnique(result, value);
|
||
}
|
||
return result;
|
||
}
|
||
function isLifecyclePilotEligible(planner) {
|
||
if (planner.selected_chain_id === "lifecycle") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const combined = `${domain} ${action}`;
|
||
return (planner.proposed_primitives.includes("query_documents") &&
|
||
(combined.includes("lifecycle") || combined.includes("activity") || combined.includes("duration") || combined.includes("age")));
|
||
}
|
||
function isDocumentEvidencePilotEligible(planner) {
|
||
if (planner.selected_chain_id === "document_evidence") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const combined = `${domain} ${action} ${unsupported}`;
|
||
return (planner.proposed_primitives.includes("query_documents") &&
|
||
(combined.includes("document") || combined.includes("list_documents")));
|
||
}
|
||
function isMovementEvidencePilotEligible(planner) {
|
||
if (planner.selected_chain_id === "movement_evidence") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||
return (planner.proposed_primitives.includes("query_movements") &&
|
||
(combined.includes("movement") ||
|
||
combined.includes("movements") ||
|
||
combined.includes("bank_operations") ||
|
||
combined.includes("movement_evidence") ||
|
||
combined.includes("list_movements")));
|
||
}
|
||
function isValueFlowPilotEligible(planner) {
|
||
if (planner.selected_chain_id === "value_flow") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const combined = `${domain} ${action} ${unsupported}`;
|
||
return (planner.proposed_primitives.includes("query_movements") &&
|
||
(combined.includes("turnover") ||
|
||
combined.includes("revenue") ||
|
||
combined.includes("payment") ||
|
||
combined.includes("payout") ||
|
||
combined.includes("value")));
|
||
}
|
||
function isMetadataPilotEligible(planner) {
|
||
if (planner.selected_chain_id === "metadata_inspection" ||
|
||
planner.selected_chain_id === "metadata_lane_clarification") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||
return (planner.proposed_primitives.includes("inspect_1c_metadata") &&
|
||
(combined.includes("metadata") ||
|
||
combined.includes("schema") ||
|
||
combined.includes("catalog") ||
|
||
combined.includes("inspect_documents") ||
|
||
combined.includes("inspect_registers") ||
|
||
combined.includes("inspect_fields")));
|
||
}
|
||
function isEntityResolutionPilotEligible(planner) {
|
||
if (planner.selected_chain_id === "entity_resolution") {
|
||
return true;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||
return (planner.proposed_primitives.includes("search_business_entity") &&
|
||
(combined.includes("entity_resolution") ||
|
||
combined.includes("search_business_entity") ||
|
||
combined.includes("entity discovery") ||
|
||
combined.includes("counterparty search")));
|
||
}
|
||
function metadataScopeForPlanner(planner) {
|
||
const entityCandidate = firstEntityCandidate(planner);
|
||
if (entityCandidate) {
|
||
return entityCandidate;
|
||
}
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const combined = `${meaning?.asked_domain_family ?? ""} ${meaning?.asked_action_family ?? ""} ${meaning?.unsupported_but_understood_family ?? ""}`
|
||
.toLowerCase()
|
||
.trim();
|
||
if (combined.includes("vat")) {
|
||
return "НДС";
|
||
}
|
||
if (combined.includes("inventory")) {
|
||
return "склад";
|
||
}
|
||
if (combined.includes("counterparty")) {
|
||
return "контрагент";
|
||
}
|
||
return null;
|
||
}
|
||
function metadataTypesForPlanner(planner) {
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
if (action === "inspect_registers") {
|
||
return ["РегистрНакопления", "РегистрСведений"];
|
||
}
|
||
if (action === "inspect_documents") {
|
||
return ["Документ"];
|
||
}
|
||
if (action === "inspect_catalog") {
|
||
return ["Справочник"];
|
||
}
|
||
return ["Документ", "РегистрНакопления", "РегистрСведений", "Справочник"];
|
||
}
|
||
function valueFlowPilotProfile(planner) {
|
||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||
const combined = `${action} ${unsupported}`;
|
||
if (combined.includes("net_value_flow") ||
|
||
combined.includes("bidirectional") ||
|
||
combined.includes("netting") ||
|
||
combined.includes("net")) {
|
||
return {
|
||
scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||
recipe_intent: null,
|
||
direction: "bidirectional_net_value_flow"
|
||
};
|
||
}
|
||
if (combined.includes("payout") ||
|
||
combined.includes("outflow") ||
|
||
combined.includes("supplier") ||
|
||
combined.includes("paid")) {
|
||
return {
|
||
scope: "counterparty_supplier_payout_query_movements_v1",
|
||
recipe_intent: "supplier_payouts_profile",
|
||
direction: "outgoing_supplier_payout"
|
||
};
|
||
}
|
||
return {
|
||
scope: "counterparty_value_flow_query_movements_v1",
|
||
recipe_intent: "customer_revenue_and_payments",
|
||
direction: "incoming_customer_revenue"
|
||
};
|
||
}
|
||
function skippedProbeResult(step, limitation) {
|
||
return {
|
||
primitive_id: step.primitive_id,
|
||
status: "skipped",
|
||
rows_received: 0,
|
||
rows_matched: 0,
|
||
limitation
|
||
};
|
||
}
|
||
function queryResultToProbeResult(primitiveId, result) {
|
||
return {
|
||
primitive_id: primitiveId,
|
||
status: result.error ? "error" : "ok",
|
||
rows_received: result.fetched_rows,
|
||
rows_matched: result.matched_rows,
|
||
limitation: result.error
|
||
};
|
||
}
|
||
function metadataResultToProbeResult(primitiveId, result) {
|
||
return {
|
||
primitive_id: primitiveId,
|
||
status: result.error ? "error" : "ok",
|
||
rows_received: result.fetched_rows,
|
||
rows_matched: result.error ? 0 : result.rows.length,
|
||
limitation: result.error
|
||
};
|
||
}
|
||
function toCoverageAwareQueryResult(result, options = {}) {
|
||
if (!result) {
|
||
return null;
|
||
}
|
||
return {
|
||
...result,
|
||
coverage_limited_by_probe_limit: options.coverageLimitedByProbeLimit ?? false,
|
||
coverage_recovered_by_period_chunking: options.coverageRecoveredByPeriodChunking ?? false,
|
||
period_chunking_granularity: options.periodChunkingGranularity ?? null,
|
||
period_chunk_count: options.periodChunkCount ?? 0
|
||
};
|
||
}
|
||
function monthWindowsForYear(year) {
|
||
const result = [];
|
||
for (let month = 0; month < 12; month += 1) {
|
||
const start = new Date(Date.UTC(Number(year), month, 1));
|
||
const end = new Date(Date.UTC(Number(year), month + 1, 0));
|
||
result.push({
|
||
period_from: `${start.getUTCFullYear()}-${String(start.getUTCMonth() + 1).padStart(2, "0")}-${String(start.getUTCDate()).padStart(2, "0")}`,
|
||
period_to: `${end.getUTCFullYear()}-${String(end.getUTCMonth() + 1).padStart(2, "0")}-${String(end.getUTCDate()).padStart(2, "0")}`
|
||
});
|
||
}
|
||
return result;
|
||
}
|
||
function periodWindowsForDateScope(dateScope) {
|
||
const yearMatch = dateScope?.match(/^(\d{4})$/);
|
||
if (yearMatch) {
|
||
return monthWindowsForYear(yearMatch[1]);
|
||
}
|
||
return [];
|
||
}
|
||
function mergeCoverageAwareQueryResults(results, options) {
|
||
const rawRows = results.flatMap((item) => item.raw_rows);
|
||
const rows = results.flatMap((item) => item.rows);
|
||
const errors = results.map((item) => toNonEmptyString(item.error)).filter((item) => Boolean(item));
|
||
return {
|
||
fetched_rows: results.reduce((sum, item) => sum + item.fetched_rows, 0),
|
||
matched_rows: results.reduce((sum, item) => sum + item.matched_rows, 0),
|
||
raw_rows: rawRows,
|
||
rows,
|
||
error: errors[0] ?? null,
|
||
coverage_limited_by_probe_limit: options.coverageLimitedByProbeLimit,
|
||
coverage_recovered_by_period_chunking: options.coverageRecoveredByPeriodChunking,
|
||
period_chunking_granularity: options.periodChunkingGranularity,
|
||
period_chunk_count: options.periodChunkCount
|
||
};
|
||
}
|
||
async function executeCoverageAwareValueFlowQuery(input) {
|
||
const queryLimitations = [];
|
||
const probeResults = [];
|
||
let executedProbeCount = 0;
|
||
const broadRecipePlan = input.recipePlanBuilder(input.baseFilters);
|
||
const broadResult = await input.deps.executeAddressMcpQuery({
|
||
query: broadRecipePlan.query,
|
||
limit: broadRecipePlan.limit,
|
||
account_scope: broadRecipePlan.account_scope
|
||
});
|
||
executedProbeCount += 1;
|
||
probeResults.push(queryResultToProbeResult(input.primitiveId, broadResult));
|
||
const broadCoverageLimited = !broadResult.error && broadResult.matched_rows >= input.maxRowsPerProbe;
|
||
if (broadResult.error) {
|
||
pushUnique(queryLimitations, broadResult.error);
|
||
return {
|
||
result: toCoverageAwareQueryResult(broadResult, {
|
||
coverageLimitedByProbeLimit: false
|
||
}),
|
||
probe_results: probeResults,
|
||
query_limitations: queryLimitations,
|
||
executed_probe_count: executedProbeCount
|
||
};
|
||
}
|
||
const periodWindows = periodWindowsForDateScope(input.dateScope);
|
||
if (!broadCoverageLimited || periodWindows.length === 0) {
|
||
return {
|
||
result: toCoverageAwareQueryResult(broadResult, {
|
||
coverageLimitedByProbeLimit: broadCoverageLimited
|
||
}),
|
||
probe_results: probeResults,
|
||
query_limitations: queryLimitations,
|
||
executed_probe_count: executedProbeCount
|
||
};
|
||
}
|
||
const requiredChunkProbeCount = periodWindows.length;
|
||
if (executedProbeCount + requiredChunkProbeCount > input.maxProbeCount) {
|
||
pushUnique(queryLimitations, "Requested period hit the MCP row limit, but the approved monthly recovery probe budget is smaller than the required subperiod count");
|
||
return {
|
||
result: toCoverageAwareQueryResult(broadResult, {
|
||
coverageLimitedByProbeLimit: true
|
||
}),
|
||
probe_results: probeResults,
|
||
query_limitations: queryLimitations,
|
||
executed_probe_count: executedProbeCount
|
||
};
|
||
}
|
||
const chunkResults = [];
|
||
let anyChunkLimited = false;
|
||
let anyChunkError = false;
|
||
for (const window of periodWindows) {
|
||
const chunkFilters = {
|
||
...input.baseFilters,
|
||
period_from: window.period_from,
|
||
period_to: window.period_to
|
||
};
|
||
const chunkPlan = input.recipePlanBuilder(chunkFilters);
|
||
const chunkResult = await input.deps.executeAddressMcpQuery({
|
||
query: chunkPlan.query,
|
||
limit: chunkPlan.limit,
|
||
account_scope: chunkPlan.account_scope
|
||
});
|
||
executedProbeCount += 1;
|
||
probeResults.push(queryResultToProbeResult(input.primitiveId, chunkResult));
|
||
if (chunkResult.error) {
|
||
anyChunkError = true;
|
||
pushUnique(queryLimitations, chunkResult.error);
|
||
continue;
|
||
}
|
||
if (chunkResult.matched_rows >= input.maxRowsPerProbe) {
|
||
anyChunkLimited = true;
|
||
}
|
||
chunkResults.push(chunkResult);
|
||
}
|
||
if (chunkResults.length === 0 && anyChunkError) {
|
||
return {
|
||
result: toCoverageAwareQueryResult(broadResult, {
|
||
coverageLimitedByProbeLimit: true
|
||
}),
|
||
probe_results: probeResults,
|
||
query_limitations: queryLimitations,
|
||
executed_probe_count: executedProbeCount
|
||
};
|
||
}
|
||
return {
|
||
result: mergeCoverageAwareQueryResults(chunkResults, {
|
||
coverageLimitedByProbeLimit: anyChunkLimited || anyChunkError,
|
||
coverageRecoveredByPeriodChunking: true,
|
||
periodChunkingGranularity: "month",
|
||
periodChunkCount: periodWindows.length
|
||
}),
|
||
probe_results: probeResults,
|
||
query_limitations: queryLimitations,
|
||
executed_probe_count: executedProbeCount
|
||
};
|
||
}
|
||
function summarizeLifecycleRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP document rows fetched";
|
||
}
|
||
return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched lifecycle scope`;
|
||
}
|
||
function summarizeDocumentRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP document rows fetched";
|
||
}
|
||
return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched document scope`;
|
||
}
|
||
function summarizeMovementRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP movement rows fetched";
|
||
}
|
||
return `${result.fetched_rows} MCP movement rows fetched, ${result.matched_rows} matched movement scope`;
|
||
}
|
||
function summarizeValueFlowRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP value-flow rows fetched";
|
||
}
|
||
if (result.coverage_recovered_by_period_chunking && result.period_chunking_granularity === "month") {
|
||
return `${result.period_chunk_count} monthly MCP value-flow probes fetched ${result.fetched_rows} rows total, ${result.matched_rows} matched value-flow scope after the broad probe hit the row limit`;
|
||
}
|
||
return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`;
|
||
}
|
||
function summarizeMetadataRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP metadata rows fetched";
|
||
}
|
||
return `${result.fetched_rows} MCP metadata rows fetched`;
|
||
}
|
||
function summarizeEntityResolutionRows(result) {
|
||
if (result.error) {
|
||
return null;
|
||
}
|
||
if (result.fetched_rows <= 0) {
|
||
return "0 MCP catalog rows fetched";
|
||
}
|
||
return `${result.fetched_rows} MCP catalog rows fetched for entity search`;
|
||
}
|
||
function entityResolutionFollowupStepLimitation() {
|
||
return "Entity-resolution could not continue because the checked catalog search step did not return a confirmed slice";
|
||
}
|
||
function buildEntityResolutionResolveProbeResult(input) {
|
||
if (!input.resolution) {
|
||
return {
|
||
primitive_id: "resolve_entity_reference",
|
||
status: "ok",
|
||
rows_received: input.queryResult.fetched_rows,
|
||
rows_matched: 0,
|
||
limitation: null
|
||
};
|
||
}
|
||
if (input.resolution.resolution_status === "resolved") {
|
||
return {
|
||
primitive_id: "resolve_entity_reference",
|
||
status: "ok",
|
||
rows_received: input.queryResult.fetched_rows,
|
||
rows_matched: 1,
|
||
limitation: null
|
||
};
|
||
}
|
||
return {
|
||
primitive_id: "resolve_entity_reference",
|
||
status: "ok",
|
||
rows_received: input.queryResult.fetched_rows,
|
||
rows_matched: 0,
|
||
limitation: null
|
||
};
|
||
}
|
||
function buildEntityResolutionCoverageProbeResult(input) {
|
||
const resolved = input.resolution?.resolution_status === "resolved";
|
||
return {
|
||
primitive_id: "probe_coverage",
|
||
status: "ok",
|
||
rows_received: 1,
|
||
rows_matched: resolved ? 1 : 0,
|
||
limitation: null
|
||
};
|
||
}
|
||
function metadataRowText(row, keys) {
|
||
for (const key of keys) {
|
||
const text = toNonEmptyString(row[key]);
|
||
if (text) {
|
||
return text;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function metadataObjectName(row) {
|
||
return metadataRowText(row, [
|
||
"ПолноеИмя",
|
||
"full_name",
|
||
"FullName",
|
||
"Имя",
|
||
"name",
|
||
"Name",
|
||
"presentation",
|
||
"Представление",
|
||
"synonym",
|
||
"Synonym"
|
||
]);
|
||
}
|
||
function metadataEntitySet(row) {
|
||
return metadataRowText(row, [
|
||
"ТипМетаданных",
|
||
"type",
|
||
"Type",
|
||
"meta_type",
|
||
"MetaType",
|
||
"ВидМетаданных",
|
||
"kind"
|
||
]);
|
||
}
|
||
function inferMetadataEntitySetFromObjectName(objectName) {
|
||
const text = String(objectName ?? "").trim();
|
||
if (!text) {
|
||
return null;
|
||
}
|
||
const dotIndex = text.indexOf(".");
|
||
if (dotIndex <= 0) {
|
||
return null;
|
||
}
|
||
const entitySet = text.slice(0, dotIndex).trim();
|
||
return entitySet.length > 0 ? entitySet : null;
|
||
}
|
||
function metadataChildNames(value) {
|
||
if (!Array.isArray(value)) {
|
||
return [];
|
||
}
|
||
const result = [];
|
||
for (const item of value) {
|
||
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
||
continue;
|
||
}
|
||
const record = item;
|
||
const fieldName = metadataRowText(record, ["Имя", "name", "Name", "full_name", "FullName"]);
|
||
if (fieldName) {
|
||
pushUnique(result, fieldName);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
function metadataAvailableFields(rows) {
|
||
const result = [];
|
||
for (const row of rows) {
|
||
for (const field of metadataChildNames(row["Реквизиты"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["attributes"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["Attributes"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["Измерения"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["dimensions"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["Ресурсы"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
for (const field of metadataChildNames(row["resources"])) {
|
||
pushUnique(result, field);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
function normalizeMetadataEntitySetToken(value) {
|
||
return String(value ?? "")
|
||
.toLowerCase()
|
||
.replace(/[\s_.-]+/g, "");
|
||
}
|
||
function metadataMatchesRequestedType(entitySet, requestedMetaType) {
|
||
const entityToken = normalizeMetadataEntitySetToken(entitySet);
|
||
const requestedToken = normalizeMetadataEntitySetToken(requestedMetaType);
|
||
return entityToken.includes(requestedToken) || requestedToken.includes(entityToken);
|
||
}
|
||
function metadataRouteFamilyForEntitySet(entitySet) {
|
||
const token = normalizeMetadataEntitySetToken(entitySet);
|
||
if (token.includes("документ") || token.includes("document")) {
|
||
return "document_evidence";
|
||
}
|
||
if (token.includes("регистрнакопления") ||
|
||
token.includes("регистсведений") ||
|
||
token.includes("регистрсведений") ||
|
||
token.includes("accumulationregister") ||
|
||
token.includes("informationregister")) {
|
||
return "movement_evidence";
|
||
}
|
||
if (token.includes("справочник") || token.includes("catalog") || token.includes("directory")) {
|
||
return "catalog_drilldown";
|
||
}
|
||
return null;
|
||
}
|
||
function metadataNextPrimitiveForRouteFamily(routeFamily) {
|
||
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 selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes) {
|
||
const requestedMatches = availableEntitySets.filter((entitySet) => requestedMetaTypes.some((requestedMetaType) => metadataMatchesRequestedType(entitySet, requestedMetaType)));
|
||
if (requestedMatches.length === 1) {
|
||
return {
|
||
selectedEntitySet: requestedMatches[0] ?? null,
|
||
ambiguityDetected: false,
|
||
ambiguityEntitySets: []
|
||
};
|
||
}
|
||
if (requestedMatches.length > 1) {
|
||
return {
|
||
selectedEntitySet: null,
|
||
ambiguityDetected: true,
|
||
ambiguityEntitySets: requestedMatches
|
||
};
|
||
}
|
||
if (availableEntitySets.length === 1) {
|
||
return {
|
||
selectedEntitySet: availableEntitySets[0] ?? null,
|
||
ambiguityDetected: false,
|
||
ambiguityEntitySets: []
|
||
};
|
||
}
|
||
return {
|
||
selectedEntitySet: null,
|
||
ambiguityDetected: availableEntitySets.length > 1,
|
||
ambiguityEntitySets: availableEntitySets
|
||
};
|
||
}
|
||
function metadataObjectsForEntitySet(entitySet, matchedObjects) {
|
||
if (!entitySet) {
|
||
return [];
|
||
}
|
||
return matchedObjects.filter((item) => item.startsWith(`${entitySet}.`) || item.includes(entitySet));
|
||
}
|
||
function deriveMetadataSurface(result, metadataScope, requestedMetaTypes) {
|
||
if (!result || result.error || result.rows.length <= 0) {
|
||
return null;
|
||
}
|
||
const matchedObjects = [];
|
||
const availableEntitySets = [];
|
||
for (const row of result.rows) {
|
||
const objectName = metadataObjectName(row);
|
||
if (objectName) {
|
||
pushUnique(matchedObjects, objectName);
|
||
}
|
||
const entitySet = metadataEntitySet(row) ?? inferMetadataEntitySetFromObjectName(objectName);
|
||
if (entitySet) {
|
||
pushUnique(availableEntitySets, entitySet);
|
||
}
|
||
}
|
||
const grounding = selectMetadataEntityGrounding(availableEntitySets, requestedMetaTypes);
|
||
const downstreamRouteFamily = grounding.selectedEntitySet
|
||
? metadataRouteFamilyForEntitySet(grounding.selectedEntitySet)
|
||
: null;
|
||
const knownLimitations = [];
|
||
if (grounding.ambiguityDetected && grounding.ambiguityEntitySets.length > 0) {
|
||
knownLimitations.push(`Exact downstream metadata surface remains ambiguous across: ${grounding.ambiguityEntitySets.join(", ")}`);
|
||
}
|
||
return {
|
||
metadata_scope: metadataScope,
|
||
requested_meta_types: requestedMetaTypes,
|
||
matched_rows: result.rows.length,
|
||
available_entity_sets: availableEntitySets,
|
||
matched_objects: matchedObjects,
|
||
selected_entity_set: grounding.selectedEntitySet,
|
||
selected_surface_objects: metadataObjectsForEntitySet(grounding.selectedEntitySet, matchedObjects),
|
||
downstream_route_family: downstreamRouteFamily,
|
||
recommended_next_primitive: metadataNextPrimitiveForRouteFamily(downstreamRouteFamily),
|
||
ambiguity_detected: grounding.ambiguityDetected,
|
||
ambiguity_entity_sets: grounding.ambiguityEntitySets,
|
||
available_fields: metadataAvailableFields(result.rows),
|
||
known_limitations: knownLimitations,
|
||
inference_basis: "confirmed_1c_metadata_surface_rows"
|
||
};
|
||
}
|
||
function buildMetadataConfirmedFacts(surface) {
|
||
if (!surface) {
|
||
return [];
|
||
}
|
||
const facts = [];
|
||
const scopeSuffix = surface.metadata_scope ? ` for ${surface.metadata_scope}` : "";
|
||
facts.push(`Confirmed 1C metadata surface${scopeSuffix}: ${surface.matched_rows} rows and ${surface.matched_objects.length} matching objects`);
|
||
if (surface.available_entity_sets.length > 0) {
|
||
facts.push(`Available metadata object sets: ${surface.available_entity_sets.join(", ")}`);
|
||
}
|
||
if (surface.selected_entity_set) {
|
||
facts.push(`Selected metadata entity set: ${surface.selected_entity_set}`);
|
||
}
|
||
if (surface.selected_surface_objects.length > 0) {
|
||
facts.push(`Selected metadata objects: ${surface.selected_surface_objects.slice(0, 8).join(", ")}`);
|
||
}
|
||
if (surface.available_fields.length > 0) {
|
||
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
|
||
}
|
||
return facts;
|
||
}
|
||
function buildMetadataInferredFacts(surface) {
|
||
if (!surface || !surface.selected_entity_set || !surface.downstream_route_family || !surface.recommended_next_primitive) {
|
||
return [];
|
||
}
|
||
return [
|
||
`A likely next checked lane may be inferred as ${surface.downstream_route_family} from the confirmed metadata surface`
|
||
];
|
||
}
|
||
function buildMetadataUnknownFacts(surface, metadataScope) {
|
||
if (surface) {
|
||
if (surface.ambiguity_detected && surface.ambiguity_entity_sets.length > 0) {
|
||
return [...surface.known_limitations];
|
||
}
|
||
if (surface.available_fields.length > 0) {
|
||
return [];
|
||
}
|
||
return ["Detailed metadata fields were not returned by this MCP metadata probe"];
|
||
}
|
||
if (metadataScope) {
|
||
return [`No matching 1C metadata objects were confirmed for scope "${metadataScope}"`];
|
||
}
|
||
return ["No matching 1C metadata objects were confirmed by this MCP metadata probe"];
|
||
}
|
||
function buildEntityResolutionConfirmedFacts(resolution) {
|
||
if (!resolution || resolution.resolution_status !== "resolved" || !resolution.resolved_entity) {
|
||
return [];
|
||
}
|
||
if (resolution.requested_entity && normalizeEntityResolutionText(resolution.requested_entity) === normalizeEntityResolutionText(resolution.resolved_entity)) {
|
||
return [`В проверенном каталожном срезе 1С найден контрагент: ${resolution.resolved_entity}`];
|
||
}
|
||
return [
|
||
`В проверенном каталожном срезе 1С найден наиболее вероятный контрагент: ${resolution.resolved_entity}`
|
||
];
|
||
}
|
||
function buildEntityResolutionInferredFacts(resolution) {
|
||
if (!resolution) {
|
||
return [];
|
||
}
|
||
if (resolution.resolution_status === "resolved") {
|
||
const facts = ["Пока проверено только заземление сущности по каталогу 1С; документы, движения и денежные показатели еще не проверялись"];
|
||
if (resolution.requested_entity && resolution.resolved_entity) {
|
||
const requestedNormalized = normalizeEntityResolutionText(resolution.requested_entity);
|
||
const resolvedNormalized = normalizeEntityResolutionText(resolution.resolved_entity);
|
||
if (requestedNormalized !== resolvedNormalized) {
|
||
facts.push("Контрагент выбран как ближайшее подтвержденное совпадение имени в проверенном каталоге 1С");
|
||
}
|
||
}
|
||
return facts;
|
||
}
|
||
if (resolution.resolution_status === "ambiguous") {
|
||
return ["В проверенном каталожном срезе осталось несколько близких кандидатов, поэтому точного контрагента в 1С еще нужно уточнить"];
|
||
}
|
||
return [];
|
||
}
|
||
function buildEntityResolutionUnknownFacts(resolution, requestedEntity) {
|
||
if (!resolution) {
|
||
return ["По проверенному каталожному поиску 1С не удалось заземлить сущность контрагента"];
|
||
}
|
||
const unknownFacts = ["Документы, движения и денежные показатели по этому контрагенту еще не проверялись; пока был только каталожный поиск"];
|
||
if (resolution.resolution_status === "ambiguous" && resolution.ambiguity_candidates.length > 0) {
|
||
unknownFacts.unshift(`Точное заземление контрагента в 1С остается неоднозначным между вариантами: ${resolution.ambiguity_candidates.join(", ")}`);
|
||
return unknownFacts;
|
||
}
|
||
if (resolution.resolution_status === "not_found") {
|
||
unknownFacts.unshift(requestedEntity
|
||
? `В проверенном каталожном срезе 1С не подтвержден контрагент с именем "${requestedEntity}"`
|
||
: "В проверенном каталожном срезе 1С не подтвержден подходящий контрагент");
|
||
}
|
||
return unknownFacts;
|
||
}
|
||
function rowDateValue(row) {
|
||
const candidates = [
|
||
row["Период"],
|
||
row["Period"],
|
||
row["period"],
|
||
row["Дата"],
|
||
row["Date"],
|
||
row["date"]
|
||
];
|
||
for (const candidate of candidates) {
|
||
const text = toNonEmptyString(candidate);
|
||
const match = text?.match(/(\d{4})-(\d{2})-(\d{2})/);
|
||
if (match) {
|
||
return `${match[1]}-${match[2]}-${match[3]}`;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function rowAmountValue(row) {
|
||
const candidates = [
|
||
row["Сумма"],
|
||
row["РЎСѓРјРјР°"],
|
||
row["СуммаДокумента"],
|
||
row["СуммаДокумента"],
|
||
row["Amount"],
|
||
row["amount"],
|
||
row["Total"],
|
||
row["total"]
|
||
];
|
||
for (const candidate of candidates) {
|
||
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
||
return candidate;
|
||
}
|
||
const text = toNonEmptyString(candidate);
|
||
if (!text) {
|
||
continue;
|
||
}
|
||
const normalized = text
|
||
.replace(/\s+/g, "")
|
||
.replace(/\u00a0/g, "")
|
||
.replace(",", ".")
|
||
.replace(/[^\d.-]/g, "");
|
||
const parsed = Number(normalized);
|
||
if (Number.isFinite(parsed)) {
|
||
return parsed;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function monthBucketFromIsoDate(isoDate) {
|
||
const match = isoDate?.match(/^(\d{4})-(\d{2})-\d{2}$/);
|
||
return match ? `${match[1]}-${match[2]}` : null;
|
||
}
|
||
function netDirectionFromAmount(amount) {
|
||
if (amount > 0) {
|
||
return "net_incoming";
|
||
}
|
||
if (amount < 0) {
|
||
return "net_outgoing";
|
||
}
|
||
return "balanced";
|
||
}
|
||
function monthDiff(firstIsoDate, latestIsoDate) {
|
||
const first = new Date(`${firstIsoDate}T00:00:00.000Z`);
|
||
const latest = new Date(`${latestIsoDate}T00:00:00.000Z`);
|
||
if (Number.isNaN(first.getTime()) || Number.isNaN(latest.getTime()) || latest < first) {
|
||
return 0;
|
||
}
|
||
let months = (latest.getUTCFullYear() - first.getUTCFullYear()) * 12;
|
||
months += latest.getUTCMonth() - first.getUTCMonth();
|
||
if (latest.getUTCDate() < first.getUTCDate()) {
|
||
months -= 1;
|
||
}
|
||
return Math.max(0, months);
|
||
}
|
||
function formatDurationHumanRu(totalMonths) {
|
||
const years = Math.floor(totalMonths / 12);
|
||
const months = totalMonths % 12;
|
||
const parts = [];
|
||
if (years > 0) {
|
||
parts.push(`${years} ${years === 1 ? "год" : years >= 2 && years <= 4 ? "года" : "лет"}`);
|
||
}
|
||
if (months > 0) {
|
||
parts.push(`${months} ${months === 1 ? "месяц" : months >= 2 && months <= 4 ? "месяца" : "месяцев"}`);
|
||
}
|
||
return parts.length > 0 ? parts.join(" ") : "меньше месяца";
|
||
}
|
||
function deriveActivityPeriod(result) {
|
||
if (!result || result.error || result.matched_rows <= 0) {
|
||
return null;
|
||
}
|
||
const dates = result.rows
|
||
.map((row) => rowDateValue(row))
|
||
.filter((value) => Boolean(value))
|
||
.sort();
|
||
if (dates.length === 0) {
|
||
return null;
|
||
}
|
||
const first = dates[0];
|
||
const latest = dates[dates.length - 1];
|
||
const totalMonths = monthDiff(first, latest);
|
||
return {
|
||
first_activity_date: first,
|
||
latest_activity_date: latest,
|
||
matched_rows: result.matched_rows,
|
||
duration_total_months: totalMonths,
|
||
duration_years: Math.floor(totalMonths / 12),
|
||
duration_months_remainder: totalMonths % 12,
|
||
duration_human_ru: formatDurationHumanRu(totalMonths),
|
||
inference_basis: "first_and_latest_confirmed_1c_activity_rows"
|
||
};
|
||
}
|
||
function formatAmountHumanRu(amount) {
|
||
const formatted = new Intl.NumberFormat("ru-RU", {
|
||
maximumFractionDigits: 2,
|
||
minimumFractionDigits: Number.isInteger(amount) ? 0 : 2
|
||
})
|
||
.format(amount)
|
||
.replace(/\u00a0/g, " ");
|
||
return `${formatted} руб.`;
|
||
}
|
||
function deriveValueFlowMonthBreakdown(result, aggregationAxis) {
|
||
if (!result || result.error || aggregationAxis !== "month") {
|
||
return [];
|
||
}
|
||
const buckets = new Map();
|
||
for (const row of result.rows) {
|
||
const isoDate = rowDateValue(row);
|
||
const monthBucket = monthBucketFromIsoDate(isoDate);
|
||
const amount = rowAmountValue(row);
|
||
if (!monthBucket || amount === null) {
|
||
continue;
|
||
}
|
||
const current = buckets.get(monthBucket) ?? { rows_with_amount: 0, total_amount: 0 };
|
||
current.rows_with_amount += 1;
|
||
current.total_amount += amount;
|
||
buckets.set(monthBucket, current);
|
||
}
|
||
return Array.from(buckets.entries())
|
||
.sort(([left], [right]) => left.localeCompare(right))
|
||
.map(([monthBucket, bucket]) => ({
|
||
month_bucket: monthBucket,
|
||
rows_with_amount: bucket.rows_with_amount,
|
||
total_amount: bucket.total_amount,
|
||
total_amount_human_ru: formatAmountHumanRu(bucket.total_amount)
|
||
}));
|
||
}
|
||
function deriveBidirectionalValueFlowMonthBreakdown(input) {
|
||
if (input.aggregationAxis !== "month") {
|
||
return [];
|
||
}
|
||
const incomingBuckets = deriveValueFlowMonthBreakdown(input.incomingResult, "month");
|
||
const outgoingBuckets = deriveValueFlowMonthBreakdown(input.outgoingResult, "month");
|
||
const allMonthBuckets = new Set();
|
||
for (const bucket of incomingBuckets) {
|
||
allMonthBuckets.add(bucket.month_bucket);
|
||
}
|
||
for (const bucket of outgoingBuckets) {
|
||
allMonthBuckets.add(bucket.month_bucket);
|
||
}
|
||
const incomingByMonth = new Map(incomingBuckets.map((bucket) => [bucket.month_bucket, bucket]));
|
||
const outgoingByMonth = new Map(outgoingBuckets.map((bucket) => [bucket.month_bucket, bucket]));
|
||
return Array.from(allMonthBuckets)
|
||
.sort((left, right) => left.localeCompare(right))
|
||
.map((monthBucket) => {
|
||
const incoming = incomingByMonth.get(monthBucket);
|
||
const outgoing = outgoingByMonth.get(monthBucket);
|
||
const incomingAmount = incoming?.total_amount ?? 0;
|
||
const outgoingAmount = outgoing?.total_amount ?? 0;
|
||
const netAmount = incomingAmount - outgoingAmount;
|
||
return {
|
||
month_bucket: monthBucket,
|
||
incoming_total_amount: incomingAmount,
|
||
incoming_total_amount_human_ru: formatAmountHumanRu(incomingAmount),
|
||
incoming_rows_with_amount: incoming?.rows_with_amount ?? 0,
|
||
outgoing_total_amount: outgoingAmount,
|
||
outgoing_total_amount_human_ru: formatAmountHumanRu(outgoingAmount),
|
||
outgoing_rows_with_amount: outgoing?.rows_with_amount ?? 0,
|
||
net_amount: netAmount,
|
||
net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)),
|
||
net_direction: netDirectionFromAmount(netAmount)
|
||
};
|
||
});
|
||
}
|
||
function deriveValueFlow(result, counterparty, periodScope, direction, aggregationAxis) {
|
||
if (!result || result.error || result.matched_rows <= 0) {
|
||
return null;
|
||
}
|
||
let totalAmount = 0;
|
||
let rowsWithAmount = 0;
|
||
for (const row of result.rows) {
|
||
const amount = rowAmountValue(row);
|
||
if (amount !== null) {
|
||
totalAmount += amount;
|
||
rowsWithAmount += 1;
|
||
}
|
||
}
|
||
if (rowsWithAmount <= 0) {
|
||
return null;
|
||
}
|
||
const dates = result.rows
|
||
.map((row) => rowDateValue(row))
|
||
.filter((value) => Boolean(value))
|
||
.sort();
|
||
return {
|
||
value_flow_direction: direction,
|
||
counterparty,
|
||
period_scope: periodScope,
|
||
aggregation_axis: aggregationAxis,
|
||
rows_matched: result.matched_rows,
|
||
rows_with_amount: rowsWithAmount,
|
||
total_amount: totalAmount,
|
||
total_amount_human_ru: formatAmountHumanRu(totalAmount),
|
||
first_movement_date: dates[0] ?? null,
|
||
latest_movement_date: dates[dates.length - 1] ?? null,
|
||
coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit,
|
||
coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking,
|
||
period_chunking_granularity: result.period_chunking_granularity,
|
||
monthly_breakdown: deriveValueFlowMonthBreakdown(result, aggregationAxis),
|
||
inference_basis: "sum_of_confirmed_1c_value_flow_rows"
|
||
};
|
||
}
|
||
function deriveValueFlowSideSummary(result) {
|
||
if (!result || result.error || result.matched_rows <= 0) {
|
||
return {
|
||
rows_matched: 0,
|
||
rows_with_amount: 0,
|
||
total_amount: 0,
|
||
total_amount_human_ru: formatAmountHumanRu(0),
|
||
first_movement_date: null,
|
||
latest_movement_date: null,
|
||
coverage_limited_by_probe_limit: false,
|
||
coverage_recovered_by_period_chunking: false,
|
||
period_chunking_granularity: null
|
||
};
|
||
}
|
||
let totalAmount = 0;
|
||
let rowsWithAmount = 0;
|
||
for (const row of result.rows) {
|
||
const amount = rowAmountValue(row);
|
||
if (amount !== null) {
|
||
totalAmount += amount;
|
||
rowsWithAmount += 1;
|
||
}
|
||
}
|
||
const dates = result.rows
|
||
.map((row) => rowDateValue(row))
|
||
.filter((value) => Boolean(value))
|
||
.sort();
|
||
return {
|
||
rows_matched: result.matched_rows,
|
||
rows_with_amount: rowsWithAmount,
|
||
total_amount: totalAmount,
|
||
total_amount_human_ru: formatAmountHumanRu(totalAmount),
|
||
first_movement_date: dates[0] ?? null,
|
||
latest_movement_date: dates[dates.length - 1] ?? null,
|
||
coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit,
|
||
coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking,
|
||
period_chunking_granularity: result.period_chunking_granularity
|
||
};
|
||
}
|
||
function deriveBidirectionalValueFlow(input) {
|
||
const incoming = deriveValueFlowSideSummary(input.incomingResult);
|
||
const outgoing = deriveValueFlowSideSummary(input.outgoingResult);
|
||
if (incoming.rows_with_amount <= 0 && outgoing.rows_with_amount <= 0) {
|
||
return null;
|
||
}
|
||
const netAmount = incoming.total_amount - outgoing.total_amount;
|
||
return {
|
||
counterparty: input.counterparty,
|
||
period_scope: input.periodScope,
|
||
aggregation_axis: input.aggregationAxis,
|
||
incoming_customer_revenue: incoming,
|
||
outgoing_supplier_payout: outgoing,
|
||
net_amount: netAmount,
|
||
net_amount_human_ru: formatAmountHumanRu(Math.abs(netAmount)),
|
||
net_direction: netDirectionFromAmount(netAmount),
|
||
coverage_limited_by_probe_limit: incoming.coverage_limited_by_probe_limit || outgoing.coverage_limited_by_probe_limit,
|
||
coverage_recovered_by_period_chunking: incoming.coverage_recovered_by_period_chunking || outgoing.coverage_recovered_by_period_chunking,
|
||
period_chunking_granularity: incoming.period_chunking_granularity ?? outgoing.period_chunking_granularity ?? null,
|
||
monthly_breakdown: deriveBidirectionalValueFlowMonthBreakdown({
|
||
incomingResult: input.incomingResult,
|
||
outgoingResult: input.outgoingResult,
|
||
aggregationAxis: input.aggregationAxis
|
||
}),
|
||
inference_basis: "incoming_minus_outgoing_confirmed_1c_value_flow_rows"
|
||
};
|
||
}
|
||
function summarizeBidirectionalValueFlowRows(input) {
|
||
const incoming = input.incomingResult;
|
||
const outgoing = input.outgoingResult;
|
||
if (!incoming && !outgoing) {
|
||
return null;
|
||
}
|
||
const incomingSummary = incoming?.error
|
||
? "incoming value-flow query failed"
|
||
: incoming?.coverage_recovered_by_period_chunking && incoming.period_chunking_granularity === "month"
|
||
? `${incoming.period_chunk_count} monthly incoming value-flow probes fetched ${incoming.fetched_rows} rows total, ${incoming.matched_rows} matched`
|
||
: `${incoming?.fetched_rows ?? 0} incoming value-flow rows fetched, ${incoming?.matched_rows ?? 0} matched`;
|
||
const outgoingSummary = outgoing?.error
|
||
? "outgoing supplier-payout query failed"
|
||
: outgoing?.coverage_recovered_by_period_chunking && outgoing.period_chunking_granularity === "month"
|
||
? `${outgoing.period_chunk_count} monthly outgoing supplier-payout probes fetched ${outgoing.fetched_rows} rows total, ${outgoing.matched_rows} matched`
|
||
: `${outgoing?.fetched_rows ?? 0} outgoing supplier-payout rows fetched, ${outgoing?.matched_rows ?? 0} matched`;
|
||
return `${incomingSummary}; ${outgoingSummary}`;
|
||
}
|
||
function buildLifecycleConfirmedFacts(result, counterparty) {
|
||
if (result.error || result.matched_rows <= 0) {
|
||
return [];
|
||
}
|
||
return [
|
||
counterparty
|
||
? `1C activity rows were found for counterparty ${counterparty}`
|
||
: "1C activity rows were found for the requested counterparty scope"
|
||
];
|
||
}
|
||
function checkedCounterpartySuffixRu(counterparty) {
|
||
return counterparty ? ` по контрагенту ${counterparty}` : "";
|
||
}
|
||
function checkedPeriodSuffixRu(periodScope) {
|
||
return periodScope ? ` за ${periodScope}` : " в проверенном окне";
|
||
}
|
||
function uncheckedPeriodBoundaryRu(periodScope) {
|
||
return periodScope ? ` вне периода ${periodScope}` : " без явно проверенного периода";
|
||
}
|
||
function buildDocumentConfirmedFacts(result, counterparty, periodScope) {
|
||
if (result.error || result.matched_rows <= 0) {
|
||
return [];
|
||
}
|
||
return [`В 1С найдены строки документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
|
||
}
|
||
function buildMovementConfirmedFacts(result, counterparty, periodScope) {
|
||
if (result.error || result.matched_rows <= 0) {
|
||
return [];
|
||
}
|
||
return [`В 1С найдены строки движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
|
||
}
|
||
function buildValueFlowConfirmedFacts(result, counterparty, direction) {
|
||
if (result.error || result.matched_rows <= 0) {
|
||
return [];
|
||
}
|
||
if (direction === "outgoing_supplier_payout") {
|
||
return [
|
||
counterparty
|
||
? `1C supplier-payout rows were found for counterparty ${counterparty}`
|
||
: "1C supplier-payout rows were found for the requested counterparty scope"
|
||
];
|
||
}
|
||
return [
|
||
counterparty
|
||
? `1C value-flow rows were found for counterparty ${counterparty}`
|
||
: "1C value-flow rows were found for the requested counterparty scope"
|
||
];
|
||
}
|
||
function buildBidirectionalValueFlowConfirmedFacts(derived) {
|
||
if (!derived) {
|
||
return [];
|
||
}
|
||
const hasIncoming = derived.incoming_customer_revenue.rows_matched > 0;
|
||
const hasOutgoing = derived.outgoing_supplier_payout.rows_matched > 0;
|
||
if (derived.counterparty) {
|
||
return [
|
||
`1C bidirectional value-flow rows were checked for counterparty ${derived.counterparty}: incoming=${hasIncoming ? "found" : "not_found"}, outgoing=${hasOutgoing ? "found" : "not_found"}`
|
||
];
|
||
}
|
||
return [
|
||
`1C bidirectional value-flow rows were checked for the requested counterparty scope: incoming=${hasIncoming ? "found" : "not_found"}, outgoing=${hasOutgoing ? "found" : "not_found"}`
|
||
];
|
||
}
|
||
function buildLifecycleInferredFacts(result) {
|
||
if (result.error || result.fetched_rows <= 0) {
|
||
return [];
|
||
}
|
||
return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"];
|
||
}
|
||
function buildDocumentInferredFacts(result, counterparty, periodScope) {
|
||
if (result.error || result.fetched_rows <= 0) {
|
||
return [];
|
||
}
|
||
if (result.matched_rows <= 0) {
|
||
return [
|
||
`По документам${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк документов этим поиском не найдено.`
|
||
];
|
||
}
|
||
return [
|
||
`Срез документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками документов, найденными этим поиском.`
|
||
];
|
||
}
|
||
function buildMovementInferredFacts(result, counterparty, periodScope) {
|
||
if (result.error || result.fetched_rows <= 0) {
|
||
return [];
|
||
}
|
||
if (result.matched_rows <= 0) {
|
||
return [
|
||
`По движениям${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк движений этим поиском не найдено.`
|
||
];
|
||
}
|
||
return [
|
||
`Срез движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками движений, найденными этим поиском.`
|
||
];
|
||
}
|
||
function buildValueFlowInferredFacts(derived) {
|
||
if (!derived) {
|
||
return [];
|
||
}
|
||
const facts = [];
|
||
if (derived.value_flow_direction === "outgoing_supplier_payout") {
|
||
facts.push("Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows");
|
||
}
|
||
else {
|
||
facts.push("Counterparty value-flow total was calculated from confirmed 1C movement rows");
|
||
}
|
||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||
facts.push("Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit");
|
||
}
|
||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||
facts.push("Counterparty monthly value-flow breakdown was grouped by month over confirmed 1C movement rows");
|
||
}
|
||
return facts;
|
||
}
|
||
function buildBidirectionalValueFlowInferredFacts(derived) {
|
||
if (!derived) {
|
||
return [];
|
||
}
|
||
const facts = ["Counterparty net value-flow was calculated as incoming confirmed 1C rows minus outgoing confirmed 1C rows"];
|
||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||
facts.push("Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit");
|
||
}
|
||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||
facts.push("Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows");
|
||
}
|
||
return facts;
|
||
}
|
||
function buildLifecycleUnknownFacts() {
|
||
return ["Legal registration date is not proven by this MCP discovery pilot"];
|
||
}
|
||
function buildDocumentUnknownFacts(periodScope, counterparty) {
|
||
return [
|
||
`Полный исторический срез документов${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
|
||
];
|
||
}
|
||
function buildMovementUnknownFacts(periodScope, counterparty) {
|
||
return [
|
||
`Полный исторический срез движений${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
|
||
];
|
||
}
|
||
function buildValueFlowUnknownFacts(periodScope, direction, derived) {
|
||
const unknownFacts = [];
|
||
if (derived?.coverage_limited_by_probe_limit) {
|
||
unknownFacts.push("Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached");
|
||
}
|
||
if (direction === "outgoing_supplier_payout") {
|
||
unknownFacts.push(periodScope
|
||
? "Full supplier-payout amount outside the checked period is not proven by this MCP discovery pilot"
|
||
: "Full all-time supplier-payout amount is not proven without an explicit checked period");
|
||
return unknownFacts;
|
||
}
|
||
unknownFacts.push(periodScope
|
||
? "Full turnover outside the checked period is not proven by this MCP discovery pilot"
|
||
: "Full all-time turnover is not proven without an explicit checked period");
|
||
return unknownFacts;
|
||
}
|
||
function buildBidirectionalValueFlowUnknownFacts(periodScope, derived) {
|
||
const unknownFacts = [];
|
||
if (derived?.coverage_limited_by_probe_limit) {
|
||
unknownFacts.push("Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached");
|
||
}
|
||
unknownFacts.push(periodScope
|
||
? "Full bidirectional value-flow outside the checked period is not proven by this MCP discovery pilot"
|
||
: "Full all-time bidirectional value-flow is not proven without an explicit checked period");
|
||
return unknownFacts;
|
||
}
|
||
function buildEmptyEvidence(planner, dryRun, probeResults, reason) {
|
||
return (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
unknownFacts: [reason],
|
||
queryLimitations: [reason],
|
||
recommendedNextProbe: dryRun.user_facing_fallback
|
||
});
|
||
}
|
||
function pilotScopeForPlanner(planner) {
|
||
switch (planner.selected_chain_id) {
|
||
case "metadata_lane_clarification":
|
||
case "metadata_inspection":
|
||
return "metadata_inspection_v1";
|
||
case "movement_evidence":
|
||
return "counterparty_movement_evidence_query_movements_v1";
|
||
case "value_flow":
|
||
return valueFlowPilotProfile(planner).scope;
|
||
case "document_evidence":
|
||
return "counterparty_document_evidence_query_documents_v1";
|
||
case "lifecycle":
|
||
return "counterparty_lifecycle_query_documents_v1";
|
||
case "entity_resolution":
|
||
return "entity_resolution_search_v1";
|
||
}
|
||
}
|
||
function isLivePilotChainSupported(chainId) {
|
||
return true;
|
||
}
|
||
async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||
const runtimeDeps = {
|
||
...DEFAULT_DEPS,
|
||
...deps
|
||
};
|
||
const dryRun = (0, assistantMcpDiscoveryRuntimeAdapter_1.buildAssistantMcpDiscoveryRuntimeDryRun)(planner);
|
||
const reasonCodes = [...dryRun.reason_codes];
|
||
const executedPrimitives = [];
|
||
const skippedPrimitives = [];
|
||
const probeResults = [];
|
||
const queryLimitations = [];
|
||
const pilotScope = pilotScopeForPlanner(planner);
|
||
if (dryRun.adapter_status === "blocked") {
|
||
pushReason(reasonCodes, "pilot_blocked_before_mcp_execution");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot was blocked before execution");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "blocked",
|
||
pilot_scope: pilotScope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["MCP discovery pilot was blocked before execution"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
if (dryRun.adapter_status !== "dry_run_ready") {
|
||
pushReason(reasonCodes, "pilot_needs_clarification_before_mcp_execution");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot needs more scope before execution");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "skipped_needs_clarification",
|
||
pilot_scope: pilotScope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["MCP discovery pilot needs more scope before execution"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const metadataPilotEligible = isMetadataPilotEligible(planner);
|
||
const documentPilotEligible = isDocumentEvidencePilotEligible(planner);
|
||
const movementPilotEligible = isMovementEvidencePilotEligible(planner);
|
||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||
const entityResolutionPilotEligible = isEntityResolutionPilotEligible(planner);
|
||
const livePilotChainSupported = isLivePilotChainSupported(planner.selected_chain_id);
|
||
if (!livePilotChainSupported ||
|
||
(!metadataPilotEligible &&
|
||
!documentPilotEligible &&
|
||
!movementPilotEligible &&
|
||
!lifecyclePilotEligible &&
|
||
!valueFlowPilotEligible &&
|
||
!entityResolutionPilotEligible)) {
|
||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||
for (const step of dryRun.execution_steps) {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_scope_unsupported_for_live_execution"));
|
||
}
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot scope is not implemented yet");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: pilotScope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["MCP discovery pilot scope is not implemented yet"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const counterparty = firstEntityCandidate(planner);
|
||
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
||
const aggregationAxis = aggregationAxisForPlanner(planner);
|
||
if (metadataPilotEligible) {
|
||
let metadataResult = null;
|
||
const metadataScope = metadataScopeForPlanner(planner);
|
||
const requestedMetaTypes = metadataTypesForPlanner(planner);
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "inspect_1c_metadata") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_metadata_uses_only_inspect_1c_metadata"));
|
||
continue;
|
||
}
|
||
metadataResult = await runtimeDeps.executeAddressMcpMetadata({
|
||
meta_type: requestedMetaTypes,
|
||
name_mask: metadataScope ?? undefined,
|
||
limit: planner.discovery_plan.execution_budget.max_rows_per_probe
|
||
});
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(metadataResultToProbeResult(step.primitive_id, metadataResult));
|
||
if (metadataResult.error) {
|
||
pushUnique(queryLimitations, metadataResult.error);
|
||
pushReason(reasonCodes, "pilot_inspect_1c_metadata_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_inspect_1c_metadata_mcp_executed");
|
||
}
|
||
}
|
||
const sourceRowsSummary = metadataResult ? summarizeMetadataRows(metadataResult) : null;
|
||
const derivedMetadataSurface = deriveMetadataSurface(metadataResult, metadataScope, requestedMetaTypes);
|
||
if (derivedMetadataSurface) {
|
||
pushReason(reasonCodes, "pilot_derived_metadata_surface_from_confirmed_rows");
|
||
}
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: buildMetadataConfirmedFacts(derivedMetadataSurface),
|
||
inferredFacts: buildMetadataInferredFacts(derivedMetadataSurface),
|
||
unknownFacts: buildMetadataUnknownFacts(derivedMetadataSurface, metadataScope),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "inspect_1c_metadata"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: "metadata_inspection_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: derivedMetadataSurface,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
if (entityResolutionPilotEligible) {
|
||
let queryResult = null;
|
||
const requestedEntity = counterparty;
|
||
if (isLowQualityEntityResolutionAnchor(requestedEntity)) {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_anchor_missing_or_low_quality");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Entity-resolution needs a clearer counterparty name");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "skipped_needs_clarification",
|
||
pilot_scope: "entity_resolution_search_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Entity-resolution needs a clearer counterparty name"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
let derivedEntityResolution = null;
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id === "search_business_entity") {
|
||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||
query: ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT)),
|
||
limit: ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT
|
||
});
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||
if (queryResult.error) {
|
||
pushUnique(queryLimitations, queryResult.error);
|
||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_executed");
|
||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||
}
|
||
continue;
|
||
}
|
||
if (step.primitive_id === "resolve_entity_reference") {
|
||
if (!queryResult || queryResult.error) {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation()));
|
||
continue;
|
||
}
|
||
if (!derivedEntityResolution) {
|
||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||
}
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(buildEntityResolutionResolveProbeResult({
|
||
queryResult,
|
||
resolution: derivedEntityResolution
|
||
}));
|
||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||
pushReason(reasonCodes, "pilot_resolve_entity_reference_from_catalog_rows");
|
||
}
|
||
else if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||
pushReason(reasonCodes, "pilot_resolve_entity_reference_requires_clarification");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_resolve_entity_reference_not_confirmed");
|
||
}
|
||
continue;
|
||
}
|
||
if (step.primitive_id === "probe_coverage") {
|
||
if (!queryResult || queryResult.error) {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation()));
|
||
continue;
|
||
}
|
||
if (!derivedEntityResolution) {
|
||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||
}
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(buildEntityResolutionCoverageProbeResult({
|
||
resolution: derivedEntityResolution
|
||
}));
|
||
pushReason(reasonCodes, "pilot_probe_coverage_executed_for_entity_resolution");
|
||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_grounding_stable_for_downstream_probe");
|
||
}
|
||
else if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_coverage_requires_clarification");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_coverage_not_confirmed");
|
||
}
|
||
continue;
|
||
}
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_entity_resolution_step_not_implemented"));
|
||
}
|
||
const sourceRowsSummary = queryResult ? summarizeEntityResolutionRows(queryResult) : null;
|
||
if (!derivedEntityResolution && queryResult && !queryResult.error) {
|
||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||
}
|
||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||
pushReason(reasonCodes, "pilot_derived_entity_resolution_from_catalog_rows");
|
||
}
|
||
if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_ambiguity_requires_clarification");
|
||
}
|
||
if (derivedEntityResolution?.resolution_status === "not_found") {
|
||
pushReason(reasonCodes, "pilot_entity_resolution_not_found_in_checked_catalog");
|
||
}
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: buildEntityResolutionConfirmedFacts(derivedEntityResolution),
|
||
inferredFacts: buildEntityResolutionInferredFacts(derivedEntityResolution),
|
||
unknownFacts: buildEntityResolutionUnknownFacts(derivedEntityResolution, requestedEntity),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: null
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: "entity_resolution_search_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: derivedEntityResolution,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
if (documentPilotEligible) {
|
||
let queryResult = null;
|
||
const filters = buildLifecycleFilters(planner);
|
||
const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("list_documents_by_counterparty", filters);
|
||
if (!selection.selected_recipe) {
|
||
pushReason(reasonCodes, "pilot_document_recipe_not_available");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Document-evidence recipe is not available");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: "counterparty_document_evidence_query_documents_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Document-evidence recipe is not available"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters);
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "query_documents") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
||
continue;
|
||
}
|
||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||
query: recipePlan.query,
|
||
limit: recipePlan.limit,
|
||
account_scope: recipePlan.account_scope
|
||
});
|
||
executedPrimitives.push(step.primitive_id);
|
||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||
if (queryResult.error) {
|
||
pushUnique(queryLimitations, queryResult.error);
|
||
pushReason(reasonCodes, "pilot_query_documents_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_query_documents_mcp_executed");
|
||
}
|
||
}
|
||
const sourceRowsSummary = queryResult ? summarizeDocumentRows(queryResult) : null;
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty, dateScope) : [],
|
||
inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult, counterparty, dateScope) : [],
|
||
unknownFacts: buildDocumentUnknownFacts(dateScope, counterparty),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "explain_evidence_basis"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: "counterparty_document_evidence_query_documents_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
if (movementPilotEligible) {
|
||
let queryResult = null;
|
||
const filters = buildValueFlowFilters(planner);
|
||
const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("bank_operations_by_counterparty", filters);
|
||
if (!selection.selected_recipe) {
|
||
pushReason(reasonCodes, "pilot_movement_recipe_not_available");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Movement-evidence recipe is not available");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Movement-evidence recipe is not available"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters);
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "query_movements") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_movements"));
|
||
continue;
|
||
}
|
||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||
query: recipePlan.query,
|
||
limit: recipePlan.limit,
|
||
account_scope: recipePlan.account_scope
|
||
});
|
||
executedPrimitives.push(step.primitive_id);
|
||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||
if (queryResult.error) {
|
||
pushUnique(queryLimitations, queryResult.error);
|
||
pushReason(reasonCodes, "pilot_query_movements_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_query_movements_mcp_executed");
|
||
}
|
||
}
|
||
const sourceRowsSummary = queryResult ? summarizeMovementRows(queryResult) : null;
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty, dateScope) : [],
|
||
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult, counterparty, dateScope) : [],
|
||
unknownFacts: buildMovementUnknownFacts(dateScope, counterparty),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "explain_evidence_basis"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: "counterparty_movement_evidence_query_movements_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
if (valueFlowPilotEligible) {
|
||
let queryResult = null;
|
||
const filters = buildValueFlowFilters(planner);
|
||
const valueFlowProfile = valueFlowPilotProfile(planner);
|
||
if (valueFlowProfile.direction === "bidirectional_net_value_flow") {
|
||
let incomingResult = null;
|
||
let outgoingResult = null;
|
||
const incomingSelection = (0, addressRecipeCatalog_1.selectAddressRecipe)("customer_revenue_and_payments", filters);
|
||
const outgoingSelection = (0, addressRecipeCatalog_1.selectAddressRecipe)("supplier_payouts_profile", filters);
|
||
if (!incomingSelection.selected_recipe || !outgoingSelection.selected_recipe) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_value_flow_recipe_not_available");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Bidirectional value-flow recipes are not available");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: valueFlowProfile.scope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Bidirectional value-flow recipes are not available"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
pushReason(reasonCodes, "pilot_bidirectional_value_flow_recipes_selected");
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "query_movements") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_bidirectional_value_flow_uses_two_query_movements_and_derives_net"));
|
||
continue;
|
||
}
|
||
const incomingExecution = await executeCoverageAwareValueFlowQuery({
|
||
primitiveId: step.primitive_id,
|
||
recipePlanBuilder: (scopedFilters) => (0, addressRecipeCatalog_1.buildAddressRecipePlan)(incomingSelection.selected_recipe, scopedFilters),
|
||
baseFilters: filters,
|
||
dateScope,
|
||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||
deps: runtimeDeps
|
||
});
|
||
const outgoingExecution = await executeCoverageAwareValueFlowQuery({
|
||
primitiveId: step.primitive_id,
|
||
recipePlanBuilder: (scopedFilters) => (0, addressRecipeCatalog_1.buildAddressRecipePlan)(outgoingSelection.selected_recipe, scopedFilters),
|
||
baseFilters: filters,
|
||
dateScope,
|
||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||
deps: runtimeDeps
|
||
});
|
||
incomingResult = incomingExecution.result;
|
||
outgoingResult = outgoingExecution.result;
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(...incomingExecution.probe_results, ...outgoingExecution.probe_results);
|
||
for (const limitation of [...incomingExecution.query_limitations, ...outgoingExecution.query_limitations]) {
|
||
pushUnique(queryLimitations, limitation);
|
||
}
|
||
if (incomingResult?.error) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_incoming_query_movements_mcp_error");
|
||
}
|
||
if (outgoingResult?.error) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_outgoing_query_movements_mcp_error");
|
||
}
|
||
if (incomingResult?.coverage_recovered_by_period_chunking) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_incoming_monthly_period_chunking_recovered_coverage");
|
||
}
|
||
if (outgoingResult?.coverage_recovered_by_period_chunking) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage");
|
||
}
|
||
if (!incomingResult?.error || !outgoingResult?.error) {
|
||
pushReason(reasonCodes, "pilot_bidirectional_query_movements_mcp_executed");
|
||
}
|
||
}
|
||
const sourceRowsSummary = summarizeBidirectionalValueFlowRows({ incomingResult, outgoingResult });
|
||
const derivedBidirectionalValueFlow = deriveBidirectionalValueFlow({
|
||
incomingResult,
|
||
outgoingResult,
|
||
counterparty,
|
||
periodScope: dateScope,
|
||
aggregationAxis
|
||
});
|
||
if (derivedBidirectionalValueFlow) {
|
||
pushReason(reasonCodes, "pilot_derived_bidirectional_value_flow_from_confirmed_rows");
|
||
if (aggregationAxis === "month" && derivedBidirectionalValueFlow.monthly_breakdown.length > 0) {
|
||
pushReason(reasonCodes, "pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows");
|
||
}
|
||
}
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: buildBidirectionalValueFlowConfirmedFacts(derivedBidirectionalValueFlow),
|
||
inferredFacts: buildBidirectionalValueFlowInferredFacts(derivedBidirectionalValueFlow),
|
||
unknownFacts: buildBidirectionalValueFlowUnknownFacts(dateScope, derivedBidirectionalValueFlow),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "explain_evidence_basis"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: valueFlowProfile.scope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const recipeIntent = valueFlowProfile.recipe_intent;
|
||
const selection = recipeIntent ? (0, addressRecipeCatalog_1.selectAddressRecipe)(recipeIntent, filters) : { selected_recipe: null };
|
||
if (!selection.selected_recipe) {
|
||
pushReason(reasonCodes, "pilot_value_flow_recipe_not_available");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Value-flow recipe is not available");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: valueFlowProfile.scope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Value-flow recipe is not available"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
pushReason(reasonCodes, valueFlowProfile.direction === "outgoing_supplier_payout"
|
||
? "pilot_supplier_payout_recipe_selected"
|
||
: "pilot_customer_revenue_recipe_selected");
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "query_movements") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_value_flow_uses_query_movements_and_derives_aggregate"));
|
||
continue;
|
||
}
|
||
const execution = await executeCoverageAwareValueFlowQuery({
|
||
primitiveId: step.primitive_id,
|
||
recipePlanBuilder: (scopedFilters) => (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, scopedFilters),
|
||
baseFilters: filters,
|
||
dateScope,
|
||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||
deps: runtimeDeps
|
||
});
|
||
queryResult = execution.result;
|
||
pushUnique(executedPrimitives, step.primitive_id);
|
||
probeResults.push(...execution.probe_results);
|
||
for (const limitation of execution.query_limitations) {
|
||
pushUnique(queryLimitations, limitation);
|
||
}
|
||
if (queryResult?.error) {
|
||
pushReason(reasonCodes, "pilot_query_movements_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_query_movements_mcp_executed");
|
||
}
|
||
if (queryResult?.coverage_recovered_by_period_chunking) {
|
||
pushReason(reasonCodes, "pilot_monthly_period_chunking_recovered_coverage");
|
||
}
|
||
}
|
||
const sourceRowsSummary = queryResult ? summarizeValueFlowRows(queryResult) : null;
|
||
const derivedValueFlow = deriveValueFlow(queryResult, counterparty, dateScope, valueFlowProfile.direction, aggregationAxis);
|
||
if (derivedValueFlow) {
|
||
pushReason(reasonCodes, "pilot_derived_value_flow_from_confirmed_rows");
|
||
if (aggregationAxis === "month" && derivedValueFlow.monthly_breakdown.length > 0) {
|
||
pushReason(reasonCodes, "pilot_derived_value_flow_monthly_breakdown_from_confirmed_rows");
|
||
}
|
||
}
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: queryResult ? buildValueFlowConfirmedFacts(queryResult, counterparty, valueFlowProfile.direction) : [],
|
||
inferredFacts: buildValueFlowInferredFacts(derivedValueFlow),
|
||
unknownFacts: buildValueFlowUnknownFacts(dateScope, valueFlowProfile.direction, derivedValueFlow),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "explain_evidence_basis"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: valueFlowProfile.scope,
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: derivedValueFlow,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
let queryResult = null;
|
||
const filters = buildLifecycleFilters(planner);
|
||
const selection = (0, addressRecipeCatalog_1.selectAddressRecipe)("counterparty_activity_lifecycle", filters);
|
||
if (!selection.selected_recipe) {
|
||
pushReason(reasonCodes, "pilot_lifecycle_recipe_not_available");
|
||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Lifecycle recipe is not available");
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "unsupported",
|
||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: false,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: null,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: null,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: ["Lifecycle recipe is not available"],
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|
||
const recipePlan = (0, addressRecipeCatalog_1.buildAddressRecipePlan)(selection.selected_recipe, filters);
|
||
for (const step of dryRun.execution_steps) {
|
||
if (step.primitive_id !== "query_documents") {
|
||
skippedPrimitives.push(step.primitive_id);
|
||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
||
continue;
|
||
}
|
||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||
query: recipePlan.query,
|
||
limit: recipePlan.limit,
|
||
account_scope: recipePlan.account_scope
|
||
});
|
||
executedPrimitives.push(step.primitive_id);
|
||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||
if (queryResult.error) {
|
||
pushUnique(queryLimitations, queryResult.error);
|
||
pushReason(reasonCodes, "pilot_query_documents_mcp_error");
|
||
}
|
||
else {
|
||
pushReason(reasonCodes, "pilot_query_documents_mcp_executed");
|
||
}
|
||
}
|
||
const sourceRowsSummary = queryResult ? summarizeLifecycleRows(queryResult) : null;
|
||
const derivedActivityPeriod = deriveActivityPeriod(queryResult);
|
||
if (derivedActivityPeriod) {
|
||
pushReason(reasonCodes, "pilot_derived_activity_period_from_confirmed_rows");
|
||
}
|
||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||
plan: planner.discovery_plan,
|
||
probeResults,
|
||
confirmedFacts: queryResult ? buildLifecycleConfirmedFacts(queryResult, counterparty) : [],
|
||
inferredFacts: queryResult ? buildLifecycleInferredFacts(queryResult) : [],
|
||
unknownFacts: buildLifecycleUnknownFacts(),
|
||
sourceRowsSummary,
|
||
queryLimitations,
|
||
recommendedNextProbe: "explain_evidence_basis"
|
||
});
|
||
return {
|
||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||
pilot_status: "executed",
|
||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
||
dry_run: dryRun,
|
||
mcp_execution_performed: executedPrimitives.length > 0,
|
||
executed_primitives: executedPrimitives,
|
||
skipped_primitives: skippedPrimitives,
|
||
probe_results: probeResults,
|
||
evidence,
|
||
source_rows_summary: sourceRowsSummary,
|
||
derived_metadata_surface: null,
|
||
derived_entity_resolution: null,
|
||
derived_activity_period: derivedActivityPeriod,
|
||
derived_value_flow: null,
|
||
derived_bidirectional_value_flow: null,
|
||
query_limitations: queryLimitations,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|