364 lines
15 KiB
JavaScript
364 lines
15 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
|
|
};
|
|
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 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 isLifecyclePilotEligible(planner) {
|
|
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 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 summarizeRows(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 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 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 buildConfirmedFacts(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 buildInferredFacts(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 buildUnknownFacts() {
|
|
return ["Legal registration date is not proven by this MCP discovery pilot"];
|
|
}
|
|
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
|
|
});
|
|
}
|
|
async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
|
const dryRun = (0, assistantMcpDiscoveryRuntimeAdapter_1.buildAssistantMcpDiscoveryRuntimeDryRun)(planner);
|
|
const reasonCodes = [...dryRun.reason_codes];
|
|
const executedPrimitives = [];
|
|
const skippedPrimitives = [];
|
|
const probeResults = [];
|
|
const queryLimitations = [];
|
|
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: "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_activity_period: 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: "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_activity_period: null,
|
|
query_limitations: ["MCP discovery pilot needs more scope before execution"],
|
|
reason_codes: reasonCodes
|
|
};
|
|
}
|
|
if (!isLifecyclePilotEligible(planner)) {
|
|
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: "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_activity_period: null,
|
|
query_limitations: ["MCP discovery pilot scope is not implemented yet"],
|
|
reason_codes: reasonCodes
|
|
};
|
|
}
|
|
let queryResult = null;
|
|
const counterparty = firstEntityCandidate(planner);
|
|
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_activity_period: 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 deps.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 ? summarizeRows(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 ? buildConfirmedFacts(queryResult, counterparty) : [],
|
|
inferredFacts: queryResult ? buildInferredFacts(queryResult) : [],
|
|
unknownFacts: buildUnknownFacts(),
|
|
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_activity_period: derivedActivityPeriod,
|
|
query_limitations: queryLimitations,
|
|
reason_codes: reasonCodes
|
|
};
|
|
}
|