ARCH: перезапустить план на MCP bounded autonomy и добавить metadata pilot
This commit is contained in:
parent
bda7ca9cc1
commit
561b4ea45c
|
|
@ -1345,6 +1345,22 @@ Module progress:
|
||||||
|
|
||||||
- Big Block 5 MCP Semantic Data Agent: `100%`.
|
- Big Block 5 MCP Semantic Data Agent: `100%`.
|
||||||
|
|
||||||
|
## Reset Hand-Off - 2026-04-21
|
||||||
|
|
||||||
|
The progress updates above closed the first guarded MCP discovery pilot wave.
|
||||||
|
|
||||||
|
They do **not** mean the strategic autonomy target is complete.
|
||||||
|
|
||||||
|
From 2026-04-21 onward the mainline continues in:
|
||||||
|
|
||||||
|
- [15 - mcp_bounded_autonomy_reset_plan_2026-04-21.md](/x:/1C/NDC_1C/docs/ARCH/11%20-%20architecture_turnaround/15%20-%20mcp_bounded_autonomy_reset_plan_2026-04-21.md:1)
|
||||||
|
|
||||||
|
That reset freezes the continuity/authority stabilization as sufficient and returns the project to the primary trajectory:
|
||||||
|
|
||||||
|
- metadata-first self-navigation;
|
||||||
|
- entity/schema grounding;
|
||||||
|
- planner-selected MCP primitive chains instead of route-per-question hardcoding.
|
||||||
|
|
||||||
## Execution Rule
|
## Execution Rule
|
||||||
|
|
||||||
Do not implement this plan as:
|
Do not implement this plan as:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
# 15 - MCP Bounded Autonomy Reset Plan (2026-04-21)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This note resets the execution focus after the stabilization wave inside turnaround `11`.
|
||||||
|
|
||||||
|
It does not cancel the continuity and authority work already done.
|
||||||
|
|
||||||
|
It clarifies that the main project trajectory is not:
|
||||||
|
|
||||||
|
- endless polishing of deterministic route arbitration;
|
||||||
|
- route-per-question hardcoding;
|
||||||
|
- a fake "agentic" mode that is actually another brittle prompt wrapper.
|
||||||
|
|
||||||
|
The real trajectory is:
|
||||||
|
|
||||||
|
- bounded assistant autonomy over reviewed MCP primitives;
|
||||||
|
- proof-first discovery of 1C evidence;
|
||||||
|
- gradual reduction of route hardcoding at the question level.
|
||||||
|
|
||||||
|
## Why The Reset Is Necessary
|
||||||
|
|
||||||
|
The previous wave repaired real defects:
|
||||||
|
|
||||||
|
- stale carryover stopped beating the current question on critical contours;
|
||||||
|
- guarded MCP discovery answers stopped being overwritten by stale exact/lifecycle paths;
|
||||||
|
- broad business evaluation no longer breaks the return into the data contour.
|
||||||
|
|
||||||
|
That work stays valid.
|
||||||
|
|
||||||
|
But it was support work around the edge of the main target.
|
||||||
|
|
||||||
|
The strategic target was always bigger:
|
||||||
|
|
||||||
|
- the assistant should not need one deterministic route per business wording;
|
||||||
|
- the assistant should be able to orient itself inside 1C through a reviewed set of MCP primitives;
|
||||||
|
- the planner should decide which safe primitive chain to use for the current data need;
|
||||||
|
- the evidence gate should decide what may be stated to the user.
|
||||||
|
|
||||||
|
So the reset is not "we were wrong".
|
||||||
|
|
||||||
|
It is:
|
||||||
|
|
||||||
|
- stabilization is now frozen as sufficient;
|
||||||
|
- the mainline returns to `MCP-first bounded autonomy`.
|
||||||
|
|
||||||
|
## What Is Frozen
|
||||||
|
|
||||||
|
The following is now baseline, not the mainline:
|
||||||
|
|
||||||
|
- current-turn meaning authority;
|
||||||
|
- continuity subordinated to current explicit meaning;
|
||||||
|
- guarded discovery response replacement;
|
||||||
|
- broad-evaluation bridge that does not destroy the next data follow-up.
|
||||||
|
|
||||||
|
These seams may still receive bug fixes if they block MCP-first execution.
|
||||||
|
|
||||||
|
They are not the main feature track anymore.
|
||||||
|
|
||||||
|
## North Star
|
||||||
|
|
||||||
|
The target is not an unrestricted free agent.
|
||||||
|
|
||||||
|
The target is a bounded planner over a reviewed primitive catalog:
|
||||||
|
|
||||||
|
1. recognize the business data need;
|
||||||
|
2. pick allowed MCP primitives;
|
||||||
|
3. execute bounded probes against 1C;
|
||||||
|
4. aggregate evidence;
|
||||||
|
5. answer only within the evidence gate.
|
||||||
|
|
||||||
|
In short:
|
||||||
|
|
||||||
|
- move determinism from `route per user wording`
|
||||||
|
- to `catalog of safe primitives + proof workflow`.
|
||||||
|
|
||||||
|
## Big Block A. Metadata-First Self-Navigation
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
Teach the assistant to inspect the 1C schema surface before guessing a route.
|
||||||
|
|
||||||
|
This is the first real step from pilot hardcoding toward self-navigation.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- live execution of `inspect_1c_metadata`;
|
||||||
|
- metadata-aware planner path that cannot collapse into `query_documents`;
|
||||||
|
- machine-readable metadata evidence:
|
||||||
|
- available object sets;
|
||||||
|
- matching objects;
|
||||||
|
- available fields/sections when metadata returns them;
|
||||||
|
- known limitations;
|
||||||
|
- human-safe answer draft for metadata discovery.
|
||||||
|
|
||||||
|
### Why This Block Comes First
|
||||||
|
|
||||||
|
Without metadata-first inspection, every later autonomy step is blind:
|
||||||
|
|
||||||
|
- entity resolution is guesswork;
|
||||||
|
- register/document choice is guesswork;
|
||||||
|
- long-tail discovery turns back into hidden route hardcoding.
|
||||||
|
|
||||||
|
### Acceptance
|
||||||
|
|
||||||
|
- raw metadata wording can bootstrap discovery input;
|
||||||
|
- planner keeps `inspect_1c_metadata` as the chosen primitive;
|
||||||
|
- pilot executes live metadata inspection through MCP;
|
||||||
|
- user-facing answer stays free of primitive/query/runtime garbage.
|
||||||
|
|
||||||
|
## Big Block B. Entity And Schema Grounding
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
Move from "I found some metadata" to "I can ground the user ask onto the right 1C surface".
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- search and resolve candidate entities through MCP instead of local tails only;
|
||||||
|
- bind metadata findings to probable document/register families;
|
||||||
|
- preserve ambiguity honestly when multiple surfaces compete;
|
||||||
|
- keep machine-readable grounding evidence for downstream probes.
|
||||||
|
|
||||||
|
### Acceptance
|
||||||
|
|
||||||
|
- assistant can say which schema surface it selected and why;
|
||||||
|
- ambiguity is surfaced as a bounded clarification, not silent route drift;
|
||||||
|
- chosen surface becomes reusable context for the next primitive.
|
||||||
|
|
||||||
|
## Big Block C. Planner-Selected Primitive Chains
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
Replace one-off pilot scopes with planner-selected safe chains.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- chain primitives such as:
|
||||||
|
- `inspect_1c_metadata`
|
||||||
|
- `search_business_entity`
|
||||||
|
- `resolve_entity_reference`
|
||||||
|
- `query_documents`
|
||||||
|
- `query_movements`
|
||||||
|
- `aggregate_by_axis`
|
||||||
|
- `probe_coverage`
|
||||||
|
- `explain_evidence_basis`
|
||||||
|
- keep exact deterministic routes as fast-paths;
|
||||||
|
- use discovery as the general path for understood long-tail questions.
|
||||||
|
|
||||||
|
### Acceptance
|
||||||
|
|
||||||
|
- new long-tail questions become answerable without adding a dedicated route for each wording;
|
||||||
|
- the planner output explains the chosen primitive chain;
|
||||||
|
- the answer gate still blocks overclaiming.
|
||||||
|
|
||||||
|
## Stage 1 Started Now
|
||||||
|
|
||||||
|
The first block is no longer just planned.
|
||||||
|
|
||||||
|
It has started in code in this pass.
|
||||||
|
|
||||||
|
Implemented in this stage:
|
||||||
|
|
||||||
|
- raw metadata wording now bootstraps discovery input;
|
||||||
|
- metadata planning stays on `inspect_1c_metadata` and no longer falls into `query_documents`;
|
||||||
|
- the pilot executor now has a live metadata inspection slice;
|
||||||
|
- the answer adapter can produce a user-safe metadata surface answer.
|
||||||
|
|
||||||
|
This stage is intentionally narrow.
|
||||||
|
|
||||||
|
It does **not** yet mean:
|
||||||
|
|
||||||
|
- unrestricted Qwen3 navigation across arbitrary 1C contours;
|
||||||
|
- automatic multi-step schema-to-entity-to-query chaining;
|
||||||
|
- hot-runtime replacement of broad assistant behavior everywhere.
|
||||||
|
|
||||||
|
It means the architecture now has the first real self-navigation primitive in production code.
|
||||||
|
|
||||||
|
## Execution Rule
|
||||||
|
|
||||||
|
From this point the project should prefer:
|
||||||
|
|
||||||
|
- adding or strengthening reviewed MCP primitives;
|
||||||
|
- planner-selected evidence workflows;
|
||||||
|
- machine-readable grounding and proof contracts.
|
||||||
|
|
||||||
|
And should avoid:
|
||||||
|
|
||||||
|
- growing another layer of hidden route hardcoding for each new wording;
|
||||||
|
- long stabilization detours unless they protect an MCP-first invariant;
|
||||||
|
- fake autonomy that bypasses the evidence gate.
|
||||||
|
|
||||||
|
## Bottom Line
|
||||||
|
|
||||||
|
Turnaround `11` is no longer only about making the assistant feel less glitchy.
|
||||||
|
|
||||||
|
The next move is larger:
|
||||||
|
|
||||||
|
- make the assistant able to look into 1C through bounded MCP discovery,
|
||||||
|
- choose its path through reviewed primitives,
|
||||||
|
- and answer from proved evidence instead of memorized route scripts.
|
||||||
|
|
@ -64,9 +64,15 @@ function isValueFlowPilot(pilot) {
|
||||||
pilot.pilot_scope === "counterparty_supplier_payout_query_movements_v1" ||
|
pilot.pilot_scope === "counterparty_supplier_payout_query_movements_v1" ||
|
||||||
pilot.pilot_scope === "counterparty_bidirectional_value_flow_query_movements_v1");
|
pilot.pilot_scope === "counterparty_bidirectional_value_flow_query_movements_v1");
|
||||||
}
|
}
|
||||||
|
function isMetadataPilot(pilot) {
|
||||||
|
return pilot.pilot_scope === "metadata_inspection_v1";
|
||||||
|
}
|
||||||
function headlineFor(mode, pilot) {
|
function headlineFor(mode, pilot) {
|
||||||
const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
const askedMonthlyBreakdown = pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
||||||
pilot.derived_value_flow?.aggregation_axis === "month";
|
pilot.derived_value_flow?.aggregation_axis === "month";
|
||||||
|
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
|
||||||
|
return "По метаданным 1С найдена доступная схема для дальнейшего безопасного поиска.";
|
||||||
|
}
|
||||||
if (askedMonthlyBreakdown && pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
|
if (askedMonthlyBreakdown && pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
|
||||||
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто и помесячная раскладка могут называться только как расчет по найденным строкам и проверенному периоду.";
|
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто и помесячная раскладка могут называться только как расчет по найденным строкам и проверенному периоду.";
|
||||||
}
|
}
|
||||||
|
|
@ -124,6 +130,10 @@ function buildMustNotClaim(pilot) {
|
||||||
claims.push("Do not claim full all-time turnover unless the checked period and coverage prove it.");
|
claims.push("Do not claim full all-time turnover unless the checked period and coverage prove it.");
|
||||||
claims.push("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
claims.push("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
||||||
}
|
}
|
||||||
|
if (isMetadataPilot(pilot)) {
|
||||||
|
claims.push("Do not present metadata surface as confirmed business data rows.");
|
||||||
|
claims.push("Do not claim a document/register exists outside the checked metadata probe results.");
|
||||||
|
}
|
||||||
if (pilot.evidence.confirmed_facts.length === 0) {
|
if (pilot.evidence.confirmed_facts.length === 0) {
|
||||||
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
||||||
}
|
}
|
||||||
|
|
@ -172,6 +182,23 @@ function derivedActivityInferenceLine(pilot) {
|
||||||
"Это вывод по данным 1С, а не юридически подтвержденный возраст регистрации."
|
"Это вывод по данным 1С, а не юридически подтвержденный возраст регистрации."
|
||||||
].join(" ");
|
].join(" ");
|
||||||
}
|
}
|
||||||
|
function derivedMetadataConfirmedLine(pilot) {
|
||||||
|
const surface = pilot.derived_metadata_surface;
|
||||||
|
if (!surface) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const scope = surface.metadata_scope ? ` по области "${surface.metadata_scope}"` : "";
|
||||||
|
const entitySets = surface.available_entity_sets.length > 0
|
||||||
|
? ` Типы объектов: ${surface.available_entity_sets.join(", ")}.`
|
||||||
|
: "";
|
||||||
|
const objects = surface.matched_objects.length > 0
|
||||||
|
? ` Найденные объекты: ${surface.matched_objects.slice(0, 8).join(", ")}.`
|
||||||
|
: "";
|
||||||
|
const fields = surface.available_fields.length > 0
|
||||||
|
? ` Доступные поля/секции: ${surface.available_fields.slice(0, 12).join(", ")}.`
|
||||||
|
: "";
|
||||||
|
return `Подтвержденная metadata-поверхность 1С${scope}: ${surface.matched_rows} строк metadata-ответа.${entitySets}${objects}${fields}`.replace(/\s+/g, " ").trim();
|
||||||
|
}
|
||||||
function derivedValueFlowConfirmedLine(pilot) {
|
function derivedValueFlowConfirmedLine(pilot) {
|
||||||
const flow = pilot.derived_value_flow;
|
const flow = pilot.derived_value_flow;
|
||||||
if (!flow) {
|
if (!flow) {
|
||||||
|
|
@ -262,6 +289,7 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
|
||||||
const inferenceLines = derivedInferenceLine
|
const inferenceLines = derivedInferenceLine
|
||||||
? [derivedInferenceLine]
|
? [derivedInferenceLine]
|
||||||
: pilot.evidence.inferred_facts;
|
: pilot.evidence.inferred_facts;
|
||||||
|
const derivedMetadataLine = derivedMetadataConfirmedLine(pilot);
|
||||||
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ?? derivedValueFlowConfirmedLine(pilot);
|
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ?? derivedValueFlowConfirmedLine(pilot);
|
||||||
const monthlyConfirmedLines = derivedBidirectionalValueFlowMonthlyLines(pilot).length > 0
|
const monthlyConfirmedLines = derivedBidirectionalValueFlowMonthlyLines(pilot).length > 0
|
||||||
? derivedBidirectionalValueFlowMonthlyLines(pilot)
|
? derivedBidirectionalValueFlowMonthlyLines(pilot)
|
||||||
|
|
@ -271,7 +299,9 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
|
||||||
}
|
}
|
||||||
const confirmedLines = derivedValueLine
|
const confirmedLines = derivedValueLine
|
||||||
? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines]
|
? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines]
|
||||||
: pilot.evidence.confirmed_facts;
|
: derivedMetadataLine
|
||||||
|
? [...pilot.evidence.confirmed_facts, derivedMetadataLine]
|
||||||
|
: pilot.evidence.confirmed_facts;
|
||||||
return {
|
return {
|
||||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
schema_version: exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryAnswerAdapter",
|
policy_owner: "assistantMcpDiscoveryAnswerAdapter",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
|
||||||
const addressRecipeCatalog_1 = require("./addressRecipeCatalog");
|
const addressRecipeCatalog_1 = require("./addressRecipeCatalog");
|
||||||
exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = "assistant_mcp_discovery_pilot_executor_v1";
|
exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION = "assistant_mcp_discovery_pilot_executor_v1";
|
||||||
const DEFAULT_DEPS = {
|
const DEFAULT_DEPS = {
|
||||||
executeAddressMcpQuery: addressMcpClient_1.executeAddressMcpQuery
|
executeAddressMcpQuery: addressMcpClient_1.executeAddressMcpQuery,
|
||||||
|
executeAddressMcpMetadata: addressMcpClient_1.executeAddressMcpMetadata
|
||||||
};
|
};
|
||||||
function toNonEmptyString(value) {
|
function toNonEmptyString(value) {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
|
|
@ -119,6 +120,55 @@ function isValueFlowPilotEligible(planner) {
|
||||||
combined.includes("payout") ||
|
combined.includes("payout") ||
|
||||||
combined.includes("value")));
|
combined.includes("value")));
|
||||||
}
|
}
|
||||||
|
function isMetadataPilotEligible(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 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 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) {
|
function valueFlowPilotProfile(planner) {
|
||||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||||
|
|
@ -168,6 +218,15 @@ function queryResultToProbeResult(primitiveId, result) {
|
||||||
limitation: result.error
|
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 = {}) {
|
function toCoverageAwareQueryResult(result, options = {}) {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -332,6 +391,147 @@ function summarizeValueFlowRows(result) {
|
||||||
}
|
}
|
||||||
return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`;
|
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 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 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 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);
|
||||||
|
if (entitySet) {
|
||||||
|
pushUnique(availableEntitySets, entitySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
metadata_scope: metadataScope,
|
||||||
|
requested_meta_types: requestedMetaTypes,
|
||||||
|
matched_rows: result.rows.length,
|
||||||
|
available_entity_sets: availableEntitySets,
|
||||||
|
matched_objects: matchedObjects,
|
||||||
|
available_fields: metadataAvailableFields(result.rows),
|
||||||
|
known_limitations: [],
|
||||||
|
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.available_fields.length > 0) {
|
||||||
|
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
|
||||||
|
}
|
||||||
|
return facts;
|
||||||
|
}
|
||||||
|
function buildMetadataUnknownFacts(surface, metadataScope) {
|
||||||
|
if (surface) {
|
||||||
|
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 rowDateValue(row) {
|
function rowDateValue(row) {
|
||||||
const candidates = [
|
const candidates = [
|
||||||
row["Период"],
|
row["Период"],
|
||||||
|
|
@ -756,13 +956,27 @@ function buildEmptyEvidence(planner, dryRun, probeResults, reason) {
|
||||||
recommendedNextProbe: dryRun.user_facing_fallback
|
recommendedNextProbe: dryRun.user_facing_fallback
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function pilotScopeForPlanner(planner) {
|
||||||
|
if (isMetadataPilotEligible(planner)) {
|
||||||
|
return "metadata_inspection_v1";
|
||||||
|
}
|
||||||
|
if (isValueFlowPilotEligible(planner)) {
|
||||||
|
return valueFlowPilotProfile(planner).scope;
|
||||||
|
}
|
||||||
|
return "counterparty_lifecycle_query_documents_v1";
|
||||||
|
}
|
||||||
async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
|
const runtimeDeps = {
|
||||||
|
...DEFAULT_DEPS,
|
||||||
|
...deps
|
||||||
|
};
|
||||||
const dryRun = (0, assistantMcpDiscoveryRuntimeAdapter_1.buildAssistantMcpDiscoveryRuntimeDryRun)(planner);
|
const dryRun = (0, assistantMcpDiscoveryRuntimeAdapter_1.buildAssistantMcpDiscoveryRuntimeDryRun)(planner);
|
||||||
const reasonCodes = [...dryRun.reason_codes];
|
const reasonCodes = [...dryRun.reason_codes];
|
||||||
const executedPrimitives = [];
|
const executedPrimitives = [];
|
||||||
const skippedPrimitives = [];
|
const skippedPrimitives = [];
|
||||||
const probeResults = [];
|
const probeResults = [];
|
||||||
const queryLimitations = [];
|
const queryLimitations = [];
|
||||||
|
const pilotScope = pilotScopeForPlanner(planner);
|
||||||
if (dryRun.adapter_status === "blocked") {
|
if (dryRun.adapter_status === "blocked") {
|
||||||
pushReason(reasonCodes, "pilot_blocked_before_mcp_execution");
|
pushReason(reasonCodes, "pilot_blocked_before_mcp_execution");
|
||||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot was blocked before execution");
|
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "MCP discovery pilot was blocked before execution");
|
||||||
|
|
@ -770,7 +984,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "blocked",
|
pilot_status: "blocked",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -778,6 +992,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -792,7 +1007,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "skipped_needs_clarification",
|
pilot_status: "skipped_needs_clarification",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -800,6 +1015,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -807,9 +1023,10 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
reason_codes: reasonCodes
|
reason_codes: reasonCodes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const metadataPilotEligible = isMetadataPilotEligible(planner);
|
||||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||||
if (!lifecyclePilotEligible && !valueFlowPilotEligible) {
|
if (!metadataPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||||
for (const step of dryRun.execution_steps) {
|
for (const step of dryRun.execution_steps) {
|
||||||
skippedPrimitives.push(step.primitive_id);
|
skippedPrimitives.push(step.primitive_id);
|
||||||
|
|
@ -820,7 +1037,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "unsupported",
|
pilot_status: "unsupported",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -828,6 +1045,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -838,6 +1056,65 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
const counterparty = firstEntityCandidate(planner);
|
const counterparty = firstEntityCandidate(planner);
|
||||||
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
||||||
const aggregationAxis = aggregationAxisForPlanner(planner);
|
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),
|
||||||
|
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_activity_period: null,
|
||||||
|
derived_value_flow: null,
|
||||||
|
derived_bidirectional_value_flow: null,
|
||||||
|
query_limitations: queryLimitations,
|
||||||
|
reason_codes: reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
if (valueFlowPilotEligible) {
|
if (valueFlowPilotEligible) {
|
||||||
let queryResult = null;
|
let queryResult = null;
|
||||||
const filters = buildValueFlowFilters(planner);
|
const filters = buildValueFlowFilters(planner);
|
||||||
|
|
@ -862,6 +1139,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -883,7 +1161,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
const outgoingExecution = await executeCoverageAwareValueFlowQuery({
|
const outgoingExecution = await executeCoverageAwareValueFlowQuery({
|
||||||
primitiveId: step.primitive_id,
|
primitiveId: step.primitive_id,
|
||||||
|
|
@ -892,7 +1170,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
incomingResult = incomingExecution.result;
|
incomingResult = incomingExecution.result;
|
||||||
outgoingResult = outgoingExecution.result;
|
outgoingResult = outgoingExecution.result;
|
||||||
|
|
@ -953,6 +1231,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
||||||
|
|
@ -977,6 +1256,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1000,7 +1280,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
queryResult = execution.result;
|
queryResult = execution.result;
|
||||||
pushUnique(executedPrimitives, step.primitive_id);
|
pushUnique(executedPrimitives, step.primitive_id);
|
||||||
|
|
@ -1048,6 +1328,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: derivedValueFlow,
|
derived_value_flow: derivedValueFlow,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1073,6 +1354,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1087,7 +1369,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
queryResult = await deps.executeAddressMcpQuery({
|
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||||
query: recipePlan.query,
|
query: recipePlan.query,
|
||||||
limit: recipePlan.limit,
|
limit: recipePlan.limit,
|
||||||
account_scope: recipePlan.account_scope
|
account_scope: recipePlan.account_scope
|
||||||
|
|
@ -1129,6 +1411,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: derivedActivityPeriod,
|
derived_activity_period: derivedActivityPeriod,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
|
||||||
|
|
@ -98,15 +98,6 @@ function recipeFor(input) {
|
||||||
: "planner_selected_value_flow_recipe"
|
: "planner_selected_value_flow_recipe"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (includesAny(combined, ["document", "documents"])) {
|
|
||||||
pushUnique(axes, "coverage_target");
|
|
||||||
return {
|
|
||||||
semanticDataNeed: "document evidence",
|
|
||||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
|
||||||
axes,
|
|
||||||
reason: "planner_selected_document_recipe"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
|
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
|
||||||
pushUnique(axes, "document_date");
|
pushUnique(axes, "document_date");
|
||||||
pushUnique(axes, "coverage_target");
|
pushUnique(axes, "coverage_target");
|
||||||
|
|
@ -127,6 +118,15 @@ function recipeFor(input) {
|
||||||
reason: "planner_selected_metadata_recipe"
|
reason: "planner_selected_metadata_recipe"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (includesAny(combined, ["document", "documents"])) {
|
||||||
|
pushUnique(axes, "coverage_target");
|
||||||
|
return {
|
||||||
|
semanticDataNeed: "document evidence",
|
||||||
|
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||||
|
axes,
|
||||||
|
reason: "planner_selected_document_recipe"
|
||||||
|
};
|
||||||
|
}
|
||||||
if (hasEntity(meaning)) {
|
if (hasEntity(meaning)) {
|
||||||
pushUnique(axes, "business_entity");
|
pushUnique(axes, "business_entity");
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,28 @@ function hasBidirectionalValueFlowSignal(text) {
|
||||||
function hasMonthlyAggregationSignal(text) {
|
function hasMonthlyAggregationSignal(text) {
|
||||||
return /(?:\u043f\u043e\s+\u043c\u0435\u0441\u044f\u0446\u0430\u043c|\u043f\u043e\u043c\u0435\u0441\u044f\u0447\u043d\u043e|\u0435\u0436\u0435\u043c\u0435\u0441\u044f\u0447\u043d\u043e|month\s+by\s+month|by\s+month|monthly)/iu.test(text);
|
return /(?:\u043f\u043e\s+\u043c\u0435\u0441\u044f\u0446\u0430\u043c|\u043f\u043e\u043c\u0435\u0441\u044f\u0447\u043d\u043e|\u0435\u0436\u0435\u043c\u0435\u0441\u044f\u0447\u043d\u043e|month\s+by\s+month|by\s+month|monthly)/iu.test(text);
|
||||||
}
|
}
|
||||||
|
function hasMetadataSignal(text) {
|
||||||
|
if (/(?:\u043c\u0435\u0442\u0430\u0434\u0430\u043d|schema|catalog|metadata\s+surface|\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440[\u0430\u044b]\s+1\u0441|\u0441\u0445\u0435\u043c[\u0430\u044b]\s+1\u0441)/iu.test(text)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (/(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(text) &&
|
||||||
|
/(?:\u0435\u0441\u0442\u044c|\u0434\u043e\u0441\u0442\u0443\u043f\u043d|\u0432\s+1\u0441|available|exist)/iu.test(text));
|
||||||
|
}
|
||||||
|
function metadataActionFromRawText(text) {
|
||||||
|
if (/(?:\u043f\u043e\u043b(?:\u0435|\u044f)|field)/iu.test(text)) {
|
||||||
|
return "inspect_fields";
|
||||||
|
}
|
||||||
|
if (/(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440|register)/iu.test(text)) {
|
||||||
|
return "inspect_registers";
|
||||||
|
}
|
||||||
|
if (/(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|document)/iu.test(text)) {
|
||||||
|
return "inspect_documents";
|
||||||
|
}
|
||||||
|
if (/(?:\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a|directory|catalog)/iu.test(text)) {
|
||||||
|
return "inspect_catalog";
|
||||||
|
}
|
||||||
|
return "inspect_catalog";
|
||||||
|
}
|
||||||
function hasExplicitDateScopeLiteral(text) {
|
function hasExplicitDateScopeLiteral(text) {
|
||||||
return /(?:\b(?:19|20)\d{2}\b|\b\d{4}-\d{2}-\d{2}\b|\b\d{4}-\d{2}\b)/iu.test(text);
|
return /(?:\b(?:19|20)\d{2}\b|\b\d{4}-\d{2}-\d{2}\b|\b\d{4}-\d{2}\b)/iu.test(text);
|
||||||
}
|
}
|
||||||
|
|
@ -240,6 +262,9 @@ function collectDateScopeFromRawText(text) {
|
||||||
}
|
}
|
||||||
function semanticNeedFor(input) {
|
function semanticNeedFor(input) {
|
||||||
const combined = compactLower(`${input.domain ?? ""} ${input.action ?? ""} ${input.unsupported ?? ""}`);
|
const combined = compactLower(`${input.domain ?? ""} ${input.action ?? ""} ${input.unsupported ?? ""}`);
|
||||||
|
if (input.metadataSignal || /(?:metadata|schema|catalog|inspect_(?:catalog|documents|registers|fields))/iu.test(combined)) {
|
||||||
|
return "1C metadata evidence";
|
||||||
|
}
|
||||||
if (input.lifecycleSignal || /(?:lifecycle|activity|duration|age)/iu.test(combined)) {
|
if (input.lifecycleSignal || /(?:lifecycle|activity|duration|age)/iu.test(combined)) {
|
||||||
return "counterparty lifecycle evidence";
|
return "counterparty lifecycle evidence";
|
||||||
}
|
}
|
||||||
|
|
@ -249,15 +274,15 @@ function semanticNeedFor(input) {
|
||||||
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
||||||
return "document evidence";
|
return "document evidence";
|
||||||
}
|
}
|
||||||
if (/(?:metadata|schema|catalog)/iu.test(combined)) {
|
|
||||||
return "1C metadata evidence";
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function shouldRunDiscovery(input) {
|
function shouldRunDiscovery(input) {
|
||||||
if (input.lifecycleSignal || input.unsupported) {
|
if (input.lifecycleSignal || input.unsupported) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (input.metadataSignal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (input.valueFlowSignal && !input.explicitIntentCandidate) {
|
if (input.valueFlowSignal && !input.explicitIntentCandidate) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -280,6 +305,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
const rawLifecycleSignal = hasLifecycleSignal(rawText);
|
const rawLifecycleSignal = hasLifecycleSignal(rawText);
|
||||||
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
||||||
const rawValueFlowSignal = !rawLifecycleSignal && (hasValueFlowSignal(rawText) || rawBidirectionalValueFlowSignal);
|
const rawValueFlowSignal = !rawLifecycleSignal && (hasValueFlowSignal(rawText) || rawBidirectionalValueFlowSignal);
|
||||||
|
const rawMetadataSignal = !rawLifecycleSignal && !rawValueFlowSignal && hasMetadataSignal(rawText);
|
||||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||||
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
||||||
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
||||||
|
|
@ -311,7 +337,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
action: rawAction ?? seededAction,
|
action: rawAction ?? seededAction,
|
||||||
unsupported: unsupported ?? seededUnsupported,
|
unsupported: unsupported ?? seededUnsupported,
|
||||||
lifecycleSignal,
|
lifecycleSignal,
|
||||||
valueFlowSignal
|
valueFlowSignal,
|
||||||
|
metadataSignal: rawMetadataSignal
|
||||||
});
|
});
|
||||||
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
||||||
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
||||||
|
|
@ -329,7 +356,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
? "counterparty_lifecycle"
|
? "counterparty_lifecycle"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
? "counterparty_value"
|
? "counterparty_value"
|
||||||
: rawDomain ?? seededDomain,
|
: rawMetadataSignal
|
||||||
|
? "metadata"
|
||||||
|
: rawDomain ?? seededDomain,
|
||||||
asked_action_family: lifecycleSignal
|
asked_action_family: lifecycleSignal
|
||||||
? "activity_duration"
|
? "activity_duration"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
|
|
@ -338,7 +367,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
: payoutSignal
|
: payoutSignal
|
||||||
? "payout"
|
? "payout"
|
||||||
: rawAction ?? seededAction ?? "turnover"
|
: rawAction ?? seededAction ?? "turnover"
|
||||||
: rawAction ?? seededAction,
|
: rawMetadataSignal
|
||||||
|
? metadataActionFromRawText(rawText)
|
||||||
|
: rawAction ?? seededAction,
|
||||||
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
||||||
explicit_entity_candidates: entityCandidates,
|
explicit_entity_candidates: entityCandidates,
|
||||||
explicit_organization_scope: explicitOrganizationScope,
|
explicit_organization_scope: explicitOrganizationScope,
|
||||||
|
|
@ -352,13 +383,16 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
: payoutSignal
|
: payoutSignal
|
||||||
? "counterparty_payouts_or_outflow"
|
? "counterparty_payouts_or_outflow"
|
||||||
: seededUnsupported ?? "counterparty_value_or_turnover"
|
: seededUnsupported ?? "counterparty_value_or_turnover"
|
||||||
: followupDiscoverySeedApplicable
|
: rawMetadataSignal
|
||||||
? seededUnsupported
|
? "1c_metadata_surface"
|
||||||
: null),
|
: followupDiscoverySeedApplicable
|
||||||
|
? seededUnsupported
|
||||||
|
: null),
|
||||||
stale_replay_forbidden: Boolean(assistantTurnMeaning?.stale_replay_forbidden ||
|
stale_replay_forbidden: Boolean(assistantTurnMeaning?.stale_replay_forbidden ||
|
||||||
unsupported ||
|
unsupported ||
|
||||||
lifecycleSignal ||
|
lifecycleSignal ||
|
||||||
valueFlowSignal ||
|
valueFlowSignal ||
|
||||||
|
rawMetadataSignal ||
|
||||||
followupDiscoverySeedApplicable)
|
followupDiscoverySeedApplicable)
|
||||||
};
|
};
|
||||||
const cleanTurnMeaning = {};
|
const cleanTurnMeaning = {};
|
||||||
|
|
@ -390,6 +424,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
unsupported: unsupported ?? seededUnsupported,
|
unsupported: unsupported ?? seededUnsupported,
|
||||||
lifecycleSignal,
|
lifecycleSignal,
|
||||||
valueFlowSignal,
|
valueFlowSignal,
|
||||||
|
metadataSignal: rawMetadataSignal,
|
||||||
semanticDataNeed,
|
semanticDataNeed,
|
||||||
explicitIntentCandidate,
|
explicitIntentCandidate,
|
||||||
followupDiscoverySeedApplicable
|
followupDiscoverySeedApplicable
|
||||||
|
|
@ -405,13 +440,18 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
? "raw_text"
|
? "raw_text"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
? "raw_text"
|
? "raw_text"
|
||||||
: "none";
|
: rawMetadataSignal
|
||||||
|
? "raw_text"
|
||||||
|
: "none";
|
||||||
if (lifecycleSignal) {
|
if (lifecycleSignal) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_lifecycle_signal_detected");
|
pushReason(reasonCodes, "mcp_discovery_lifecycle_signal_detected");
|
||||||
}
|
}
|
||||||
if (valueFlowSignal) {
|
if (valueFlowSignal) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_value_flow_signal_detected");
|
pushReason(reasonCodes, "mcp_discovery_value_flow_signal_detected");
|
||||||
}
|
}
|
||||||
|
if (rawMetadataSignal) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_metadata_signal_detected");
|
||||||
|
}
|
||||||
if (payoutSignal) {
|
if (payoutSignal) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,17 @@ function isValueFlowPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): b
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMetadataPilot(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean {
|
||||||
|
return pilot.pilot_scope === "metadata_inspection_v1";
|
||||||
|
}
|
||||||
|
|
||||||
function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpDiscoveryPilotExecutionContract): string {
|
function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpDiscoveryPilotExecutionContract): string {
|
||||||
const askedMonthlyBreakdown =
|
const askedMonthlyBreakdown =
|
||||||
pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
pilot.derived_bidirectional_value_flow?.aggregation_axis === "month" ||
|
||||||
pilot.derived_value_flow?.aggregation_axis === "month";
|
pilot.derived_value_flow?.aggregation_axis === "month";
|
||||||
|
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
|
||||||
|
return "По метаданным 1С найдена доступная схема для дальнейшего безопасного поиска.";
|
||||||
|
}
|
||||||
if (askedMonthlyBreakdown && pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
|
if (askedMonthlyBreakdown && pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
|
||||||
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто и помесячная раскладка могут называться только как расчет по найденным строкам и проверенному периоду.";
|
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто и помесячная раскладка могут называться только как расчет по найденным строкам и проверенному периоду.";
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +167,10 @@ function buildMustNotClaim(pilot: AssistantMcpDiscoveryPilotExecutionContract):
|
||||||
claims.push("Do not claim full all-time turnover unless the checked period and coverage prove it.");
|
claims.push("Do not claim full all-time turnover unless the checked period and coverage prove it.");
|
||||||
claims.push("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
claims.push("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
||||||
}
|
}
|
||||||
|
if (isMetadataPilot(pilot)) {
|
||||||
|
claims.push("Do not present metadata surface as confirmed business data rows.");
|
||||||
|
claims.push("Do not claim a document/register exists outside the checked metadata probe results.");
|
||||||
|
}
|
||||||
if (pilot.evidence.confirmed_facts.length === 0) {
|
if (pilot.evidence.confirmed_facts.length === 0) {
|
||||||
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +224,27 @@ function derivedActivityInferenceLine(pilot: AssistantMcpDiscoveryPilotExecution
|
||||||
].join(" ");
|
].join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function derivedMetadataConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutionContract): string | null {
|
||||||
|
const surface = pilot.derived_metadata_surface;
|
||||||
|
if (!surface) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const scope = surface.metadata_scope ? ` по области "${surface.metadata_scope}"` : "";
|
||||||
|
const entitySets =
|
||||||
|
surface.available_entity_sets.length > 0
|
||||||
|
? ` Типы объектов: ${surface.available_entity_sets.join(", ")}.`
|
||||||
|
: "";
|
||||||
|
const objects =
|
||||||
|
surface.matched_objects.length > 0
|
||||||
|
? ` Найденные объекты: ${surface.matched_objects.slice(0, 8).join(", ")}.`
|
||||||
|
: "";
|
||||||
|
const fields =
|
||||||
|
surface.available_fields.length > 0
|
||||||
|
? ` Доступные поля/секции: ${surface.available_fields.slice(0, 12).join(", ")}.`
|
||||||
|
: "";
|
||||||
|
return `Подтвержденная metadata-поверхность 1С${scope}: ${surface.matched_rows} строк metadata-ответа.${entitySets}${objects}${fields}`.replace(/\s+/g, " ").trim();
|
||||||
|
}
|
||||||
|
|
||||||
function derivedValueFlowConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutionContract): string | null {
|
function derivedValueFlowConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutionContract): string | null {
|
||||||
const flow = pilot.derived_value_flow;
|
const flow = pilot.derived_value_flow;
|
||||||
if (!flow) {
|
if (!flow) {
|
||||||
|
|
@ -318,6 +350,7 @@ export function buildAssistantMcpDiscoveryAnswerDraft(
|
||||||
const inferenceLines = derivedInferenceLine
|
const inferenceLines = derivedInferenceLine
|
||||||
? [derivedInferenceLine]
|
? [derivedInferenceLine]
|
||||||
: pilot.evidence.inferred_facts;
|
: pilot.evidence.inferred_facts;
|
||||||
|
const derivedMetadataLine = derivedMetadataConfirmedLine(pilot);
|
||||||
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ?? derivedValueFlowConfirmedLine(pilot);
|
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ?? derivedValueFlowConfirmedLine(pilot);
|
||||||
const monthlyConfirmedLines =
|
const monthlyConfirmedLines =
|
||||||
derivedBidirectionalValueFlowMonthlyLines(pilot).length > 0
|
derivedBidirectionalValueFlowMonthlyLines(pilot).length > 0
|
||||||
|
|
@ -328,7 +361,9 @@ export function buildAssistantMcpDiscoveryAnswerDraft(
|
||||||
}
|
}
|
||||||
const confirmedLines = derivedValueLine
|
const confirmedLines = derivedValueLine
|
||||||
? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines]
|
? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines]
|
||||||
: pilot.evidence.confirmed_facts;
|
: derivedMetadataLine
|
||||||
|
? [...pilot.evidence.confirmed_facts, derivedMetadataLine]
|
||||||
|
: pilot.evidence.confirmed_facts;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
schema_version: ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
schema_version: ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
executeAddressMcpMetadata,
|
||||||
executeAddressMcpQuery,
|
executeAddressMcpQuery,
|
||||||
type AddressMcpMetadataRowsResult
|
type AddressMcpMetadataRowsResult
|
||||||
} from "./addressMcpClient";
|
} from "./addressMcpClient";
|
||||||
|
|
@ -26,7 +27,13 @@ export type AssistantMcpDiscoveryPilotStatus =
|
||||||
| "unsupported";
|
| "unsupported";
|
||||||
|
|
||||||
export interface AssistantMcpDiscoveryPilotExecutorDeps {
|
export interface AssistantMcpDiscoveryPilotExecutorDeps {
|
||||||
|
executeAddressMcpQuery?: typeof executeAddressMcpQuery;
|
||||||
|
executeAddressMcpMetadata?: typeof executeAddressMcpMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResolvedAssistantMcpDiscoveryPilotExecutorDeps {
|
||||||
executeAddressMcpQuery: typeof executeAddressMcpQuery;
|
executeAddressMcpQuery: typeof executeAddressMcpQuery;
|
||||||
|
executeAddressMcpMetadata: typeof executeAddressMcpMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AssistantMcpDiscoveryDerivedActivityPeriod {
|
export interface AssistantMcpDiscoveryDerivedActivityPeriod {
|
||||||
|
|
@ -109,6 +116,17 @@ export interface AssistantMcpDiscoveryDerivedBidirectionalValueFlow {
|
||||||
inference_basis: "incoming_minus_outgoing_confirmed_1c_value_flow_rows";
|
inference_basis: "incoming_minus_outgoing_confirmed_1c_value_flow_rows";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AssistantMcpDiscoveryDerivedMetadataSurface {
|
||||||
|
metadata_scope: string | null;
|
||||||
|
requested_meta_types: string[];
|
||||||
|
matched_rows: number;
|
||||||
|
available_entity_sets: string[];
|
||||||
|
matched_objects: string[];
|
||||||
|
available_fields: string[];
|
||||||
|
known_limitations: string[];
|
||||||
|
inference_basis: "confirmed_1c_metadata_surface_rows";
|
||||||
|
}
|
||||||
|
|
||||||
interface AssistantMcpDiscoveryCoverageAwareQueryResult extends AddressMcpQueryExecutorResult {
|
interface AssistantMcpDiscoveryCoverageAwareQueryResult extends AddressMcpQueryExecutorResult {
|
||||||
coverage_limited_by_probe_limit: boolean;
|
coverage_limited_by_probe_limit: boolean;
|
||||||
coverage_recovered_by_period_chunking: boolean;
|
coverage_recovered_by_period_chunking: boolean;
|
||||||
|
|
@ -124,6 +142,7 @@ interface AssistantMcpDiscoveryCoverageAwareQueryExecution {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AssistantMcpDiscoveryPilotScope =
|
export type AssistantMcpDiscoveryPilotScope =
|
||||||
|
| "metadata_inspection_v1"
|
||||||
| "counterparty_lifecycle_query_documents_v1"
|
| "counterparty_lifecycle_query_documents_v1"
|
||||||
| "counterparty_value_flow_query_movements_v1"
|
| "counterparty_value_flow_query_movements_v1"
|
||||||
| "counterparty_supplier_payout_query_movements_v1"
|
| "counterparty_supplier_payout_query_movements_v1"
|
||||||
|
|
@ -141,6 +160,7 @@ export interface AssistantMcpDiscoveryPilotExecutionContract {
|
||||||
probe_results: AssistantMcpDiscoveryProbeResult[];
|
probe_results: AssistantMcpDiscoveryProbeResult[];
|
||||||
evidence: AssistantMcpDiscoveryEvidenceContract;
|
evidence: AssistantMcpDiscoveryEvidenceContract;
|
||||||
source_rows_summary: string | null;
|
source_rows_summary: string | null;
|
||||||
|
derived_metadata_surface: AssistantMcpDiscoveryDerivedMetadataSurface | null;
|
||||||
derived_activity_period: AssistantMcpDiscoveryDerivedActivityPeriod | null;
|
derived_activity_period: AssistantMcpDiscoveryDerivedActivityPeriod | null;
|
||||||
derived_value_flow: AssistantMcpDiscoveryDerivedValueFlow | null;
|
derived_value_flow: AssistantMcpDiscoveryDerivedValueFlow | null;
|
||||||
derived_bidirectional_value_flow: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null;
|
derived_bidirectional_value_flow: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null;
|
||||||
|
|
@ -150,8 +170,9 @@ export interface AssistantMcpDiscoveryPilotExecutionContract {
|
||||||
|
|
||||||
type AddressMcpQueryExecutorResult = Awaited<ReturnType<typeof executeAddressMcpQuery>>;
|
type AddressMcpQueryExecutorResult = Awaited<ReturnType<typeof executeAddressMcpQuery>>;
|
||||||
|
|
||||||
const DEFAULT_DEPS: AssistantMcpDiscoveryPilotExecutorDeps = {
|
const DEFAULT_DEPS: ResolvedAssistantMcpDiscoveryPilotExecutorDeps = {
|
||||||
executeAddressMcpQuery
|
executeAddressMcpQuery,
|
||||||
|
executeAddressMcpMetadata
|
||||||
};
|
};
|
||||||
|
|
||||||
function toNonEmptyString(value: unknown): string | null {
|
function toNonEmptyString(value: unknown): string | null {
|
||||||
|
|
@ -280,6 +301,60 @@ function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMetadataPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||||
|
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 metadataScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string | null {
|
||||||
|
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: AssistantMcpDiscoveryPlannerContract): string[] {
|
||||||
|
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 ["Документ", "РегистрНакопления", "РегистрСведений", "Справочник"];
|
||||||
|
}
|
||||||
|
|
||||||
interface ValueFlowPilotProfile {
|
interface ValueFlowPilotProfile {
|
||||||
scope: Extract<
|
scope: Extract<
|
||||||
AssistantMcpDiscoveryPilotScope,
|
AssistantMcpDiscoveryPilotScope,
|
||||||
|
|
@ -350,6 +425,19 @@ function queryResultToProbeResult(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function metadataResultToProbeResult(
|
||||||
|
primitiveId: string,
|
||||||
|
result: AddressMcpMetadataRowsResult
|
||||||
|
): AssistantMcpDiscoveryProbeResult {
|
||||||
|
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(
|
function toCoverageAwareQueryResult(
|
||||||
result: AddressMcpQueryExecutorResult | null,
|
result: AddressMcpQueryExecutorResult | null,
|
||||||
options: {
|
options: {
|
||||||
|
|
@ -428,7 +516,7 @@ async function executeCoverageAwareValueFlowQuery(input: {
|
||||||
dateScope: string | null;
|
dateScope: string | null;
|
||||||
maxProbeCount: number;
|
maxProbeCount: number;
|
||||||
maxRowsPerProbe: number;
|
maxRowsPerProbe: number;
|
||||||
deps: AssistantMcpDiscoveryPilotExecutorDeps;
|
deps: ResolvedAssistantMcpDiscoveryPilotExecutorDeps;
|
||||||
}): Promise<AssistantMcpDiscoveryCoverageAwareQueryExecution> {
|
}): Promise<AssistantMcpDiscoveryCoverageAwareQueryExecution> {
|
||||||
const queryLimitations: string[] = [];
|
const queryLimitations: string[] = [];
|
||||||
const probeResults: AssistantMcpDiscoveryProbeResult[] = [];
|
const probeResults: AssistantMcpDiscoveryProbeResult[] = [];
|
||||||
|
|
@ -560,6 +648,167 @@ function summarizeValueFlowRows(result: AssistantMcpDiscoveryCoverageAwareQueryR
|
||||||
return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`;
|
return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function summarizeMetadataRows(result: AddressMcpMetadataRowsResult): string | null {
|
||||||
|
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 metadataRowText(row: Record<string, unknown>, keys: string[]): string | null {
|
||||||
|
for (const key of keys) {
|
||||||
|
const text = toNonEmptyString(row[key]);
|
||||||
|
if (text) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadataObjectName(row: Record<string, unknown>): string | null {
|
||||||
|
return metadataRowText(row, [
|
||||||
|
"ПолноеИмя",
|
||||||
|
"full_name",
|
||||||
|
"FullName",
|
||||||
|
"Имя",
|
||||||
|
"name",
|
||||||
|
"Name",
|
||||||
|
"presentation",
|
||||||
|
"Представление",
|
||||||
|
"synonym",
|
||||||
|
"Synonym"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadataEntitySet(row: Record<string, unknown>): string | null {
|
||||||
|
return metadataRowText(row, [
|
||||||
|
"ТипМетаданных",
|
||||||
|
"type",
|
||||||
|
"Type",
|
||||||
|
"meta_type",
|
||||||
|
"MetaType",
|
||||||
|
"ВидМетаданных",
|
||||||
|
"kind"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadataChildNames(value: unknown): string[] {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result: string[] = [];
|
||||||
|
for (const item of value) {
|
||||||
|
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const record = item as Record<string, unknown>;
|
||||||
|
const fieldName = metadataRowText(record, ["Имя", "name", "Name", "full_name", "FullName"]);
|
||||||
|
if (fieldName) {
|
||||||
|
pushUnique(result, fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadataAvailableFields(rows: Array<Record<string, unknown>>): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
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 deriveMetadataSurface(
|
||||||
|
result: AddressMcpMetadataRowsResult | null,
|
||||||
|
metadataScope: string | null,
|
||||||
|
requestedMetaTypes: string[]
|
||||||
|
): AssistantMcpDiscoveryDerivedMetadataSurface | null {
|
||||||
|
if (!result || result.error || result.rows.length <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const matchedObjects: string[] = [];
|
||||||
|
const availableEntitySets: string[] = [];
|
||||||
|
for (const row of result.rows) {
|
||||||
|
const objectName = metadataObjectName(row);
|
||||||
|
if (objectName) {
|
||||||
|
pushUnique(matchedObjects, objectName);
|
||||||
|
}
|
||||||
|
const entitySet = metadataEntitySet(row);
|
||||||
|
if (entitySet) {
|
||||||
|
pushUnique(availableEntitySets, entitySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
metadata_scope: metadataScope,
|
||||||
|
requested_meta_types: requestedMetaTypes,
|
||||||
|
matched_rows: result.rows.length,
|
||||||
|
available_entity_sets: availableEntitySets,
|
||||||
|
matched_objects: matchedObjects,
|
||||||
|
available_fields: metadataAvailableFields(result.rows),
|
||||||
|
known_limitations: [],
|
||||||
|
inference_basis: "confirmed_1c_metadata_surface_rows"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMetadataConfirmedFacts(
|
||||||
|
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null
|
||||||
|
): string[] {
|
||||||
|
if (!surface) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const facts: string[] = [];
|
||||||
|
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.available_fields.length > 0) {
|
||||||
|
facts.push(`Available metadata fields/sections: ${surface.available_fields.slice(0, 12).join(", ")}`);
|
||||||
|
}
|
||||||
|
return facts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMetadataUnknownFacts(
|
||||||
|
surface: AssistantMcpDiscoveryDerivedMetadataSurface | null,
|
||||||
|
metadataScope: string | null
|
||||||
|
): string[] {
|
||||||
|
if (surface) {
|
||||||
|
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 rowDateValue(row: Record<string, unknown>): string | null {
|
function rowDateValue(row: Record<string, unknown>): string | null {
|
||||||
const candidates = [
|
const candidates = [
|
||||||
row["Период"],
|
row["Период"],
|
||||||
|
|
@ -1072,16 +1321,31 @@ function buildEmptyEvidence(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pilotScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): AssistantMcpDiscoveryPilotScope {
|
||||||
|
if (isMetadataPilotEligible(planner)) {
|
||||||
|
return "metadata_inspection_v1";
|
||||||
|
}
|
||||||
|
if (isValueFlowPilotEligible(planner)) {
|
||||||
|
return valueFlowPilotProfile(planner).scope;
|
||||||
|
}
|
||||||
|
return "counterparty_lifecycle_query_documents_v1";
|
||||||
|
}
|
||||||
|
|
||||||
export async function executeAssistantMcpDiscoveryPilot(
|
export async function executeAssistantMcpDiscoveryPilot(
|
||||||
planner: AssistantMcpDiscoveryPlannerContract,
|
planner: AssistantMcpDiscoveryPlannerContract,
|
||||||
deps: AssistantMcpDiscoveryPilotExecutorDeps = DEFAULT_DEPS
|
deps: AssistantMcpDiscoveryPilotExecutorDeps = DEFAULT_DEPS
|
||||||
): Promise<AssistantMcpDiscoveryPilotExecutionContract> {
|
): Promise<AssistantMcpDiscoveryPilotExecutionContract> {
|
||||||
|
const runtimeDeps: ResolvedAssistantMcpDiscoveryPilotExecutorDeps = {
|
||||||
|
...DEFAULT_DEPS,
|
||||||
|
...deps
|
||||||
|
};
|
||||||
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
|
const dryRun = buildAssistantMcpDiscoveryRuntimeDryRun(planner);
|
||||||
const reasonCodes = [...dryRun.reason_codes];
|
const reasonCodes = [...dryRun.reason_codes];
|
||||||
const executedPrimitives: string[] = [];
|
const executedPrimitives: string[] = [];
|
||||||
const skippedPrimitives: string[] = [];
|
const skippedPrimitives: string[] = [];
|
||||||
const probeResults: AssistantMcpDiscoveryProbeResult[] = [];
|
const probeResults: AssistantMcpDiscoveryProbeResult[] = [];
|
||||||
const queryLimitations: string[] = [];
|
const queryLimitations: string[] = [];
|
||||||
|
const pilotScope = pilotScopeForPlanner(planner);
|
||||||
|
|
||||||
if (dryRun.adapter_status === "blocked") {
|
if (dryRun.adapter_status === "blocked") {
|
||||||
pushReason(reasonCodes, "pilot_blocked_before_mcp_execution");
|
pushReason(reasonCodes, "pilot_blocked_before_mcp_execution");
|
||||||
|
|
@ -1090,7 +1354,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "blocked",
|
pilot_status: "blocked",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -1098,6 +1362,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1113,7 +1378,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "skipped_needs_clarification",
|
pilot_status: "skipped_needs_clarification",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -1121,6 +1386,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1129,10 +1395,11 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const metadataPilotEligible = isMetadataPilotEligible(planner);
|
||||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||||
|
|
||||||
if (!lifecyclePilotEligible && !valueFlowPilotEligible) {
|
if (!metadataPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||||
for (const step of dryRun.execution_steps) {
|
for (const step of dryRun.execution_steps) {
|
||||||
skippedPrimitives.push(step.primitive_id);
|
skippedPrimitives.push(step.primitive_id);
|
||||||
|
|
@ -1143,7 +1410,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||||
pilot_status: "unsupported",
|
pilot_status: "unsupported",
|
||||||
pilot_scope: "counterparty_lifecycle_query_documents_v1",
|
pilot_scope: pilotScope,
|
||||||
dry_run: dryRun,
|
dry_run: dryRun,
|
||||||
mcp_execution_performed: false,
|
mcp_execution_performed: false,
|
||||||
executed_primitives: executedPrimitives,
|
executed_primitives: executedPrimitives,
|
||||||
|
|
@ -1151,6 +1418,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1163,6 +1431,68 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
||||||
const aggregationAxis = aggregationAxisForPlanner(planner);
|
const aggregationAxis = aggregationAxisForPlanner(planner);
|
||||||
|
|
||||||
|
if (metadataPilotEligible) {
|
||||||
|
let metadataResult: AddressMcpMetadataRowsResult | null = 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 = resolveAssistantMcpDiscoveryEvidence({
|
||||||
|
plan: planner.discovery_plan,
|
||||||
|
probeResults,
|
||||||
|
confirmedFacts: buildMetadataConfirmedFacts(derivedMetadataSurface),
|
||||||
|
unknownFacts: buildMetadataUnknownFacts(derivedMetadataSurface, metadataScope),
|
||||||
|
sourceRowsSummary,
|
||||||
|
queryLimitations,
|
||||||
|
recommendedNextProbe: "inspect_1c_metadata"
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
schema_version: 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_activity_period: null,
|
||||||
|
derived_value_flow: null,
|
||||||
|
derived_bidirectional_value_flow: null,
|
||||||
|
query_limitations: queryLimitations,
|
||||||
|
reason_codes: reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (valueFlowPilotEligible) {
|
if (valueFlowPilotEligible) {
|
||||||
let queryResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null;
|
let queryResult: AssistantMcpDiscoveryCoverageAwareQueryResult | null = null;
|
||||||
const filters = buildValueFlowFilters(planner);
|
const filters = buildValueFlowFilters(planner);
|
||||||
|
|
@ -1187,6 +1517,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1213,7 +1544,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
const outgoingExecution = await executeCoverageAwareValueFlowQuery({
|
const outgoingExecution = await executeCoverageAwareValueFlowQuery({
|
||||||
primitiveId: step.primitive_id,
|
primitiveId: step.primitive_id,
|
||||||
|
|
@ -1222,7 +1553,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
incomingResult = incomingExecution.result;
|
incomingResult = incomingExecution.result;
|
||||||
outgoingResult = outgoingExecution.result;
|
outgoingResult = outgoingExecution.result;
|
||||||
|
|
@ -1285,6 +1616,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
||||||
|
|
@ -1310,6 +1642,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1337,7 +1670,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
dateScope,
|
dateScope,
|
||||||
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
maxProbeCount: planner.discovery_plan.execution_budget.max_probe_count,
|
||||||
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
maxRowsPerProbe: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||||
deps
|
deps: runtimeDeps
|
||||||
});
|
});
|
||||||
queryResult = execution.result;
|
queryResult = execution.result;
|
||||||
pushUnique(executedPrimitives, step.primitive_id);
|
pushUnique(executedPrimitives, step.primitive_id);
|
||||||
|
|
@ -1392,6 +1725,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: derivedValueFlow,
|
derived_value_flow: derivedValueFlow,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1418,6 +1752,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: null,
|
source_rows_summary: null,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: null,
|
derived_activity_period: null,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
@ -1433,7 +1768,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
probeResults.push(skippedProbeResult(step, "pilot_only_executes_query_documents"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
queryResult = await deps.executeAddressMcpQuery({
|
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||||
query: recipePlan.query,
|
query: recipePlan.query,
|
||||||
limit: recipePlan.limit,
|
limit: recipePlan.limit,
|
||||||
account_scope: recipePlan.account_scope
|
account_scope: recipePlan.account_scope
|
||||||
|
|
@ -1476,6 +1811,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||||
probe_results: probeResults,
|
probe_results: probeResults,
|
||||||
evidence,
|
evidence,
|
||||||
source_rows_summary: sourceRowsSummary,
|
source_rows_summary: sourceRowsSummary,
|
||||||
|
derived_metadata_surface: null,
|
||||||
derived_activity_period: derivedActivityPeriod,
|
derived_activity_period: derivedActivityPeriod,
|
||||||
derived_value_flow: null,
|
derived_value_flow: null,
|
||||||
derived_bidirectional_value_flow: null,
|
derived_bidirectional_value_flow: null,
|
||||||
|
|
|
||||||
|
|
@ -148,16 +148,6 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includesAny(combined, ["document", "documents"])) {
|
|
||||||
pushUnique(axes, "coverage_target");
|
|
||||||
return {
|
|
||||||
semanticDataNeed: "document evidence",
|
|
||||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
|
||||||
axes,
|
|
||||||
reason: "planner_selected_document_recipe"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
|
if (includesAny(combined, ["lifecycle", "activity", "duration", "age"])) {
|
||||||
pushUnique(axes, "document_date");
|
pushUnique(axes, "document_date");
|
||||||
pushUnique(axes, "coverage_target");
|
pushUnique(axes, "coverage_target");
|
||||||
|
|
@ -180,6 +170,16 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (includesAny(combined, ["document", "documents"])) {
|
||||||
|
pushUnique(axes, "coverage_target");
|
||||||
|
return {
|
||||||
|
semanticDataNeed: "document evidence",
|
||||||
|
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||||
|
axes,
|
||||||
|
reason: "planner_selected_document_recipe"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (hasEntity(meaning)) {
|
if (hasEntity(meaning)) {
|
||||||
pushUnique(axes, "business_entity");
|
pushUnique(axes, "business_entity");
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,38 @@ function hasMonthlyAggregationSignal(text: string): boolean {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasMetadataSignal(text: string): boolean {
|
||||||
|
if (
|
||||||
|
/(?:\u043c\u0435\u0442\u0430\u0434\u0430\u043d|schema|catalog|metadata\s+surface|\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440[\u0430\u044b]\s+1\u0441|\u0441\u0445\u0435\u043c[\u0430\u044b]\s+1\u0441)/iu.test(
|
||||||
|
text
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
/(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438|\u043f\u043e\u043b(?:\u0435|\u044f)|registers?|documents?|catalogs?|fields?)/iu.test(
|
||||||
|
text
|
||||||
|
) &&
|
||||||
|
/(?:\u0435\u0441\u0442\u044c|\u0434\u043e\u0441\u0442\u0443\u043f\u043d|\u0432\s+1\u0441|available|exist)/iu.test(text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadataActionFromRawText(text: string): string {
|
||||||
|
if (/(?:\u043f\u043e\u043b(?:\u0435|\u044f)|field)/iu.test(text)) {
|
||||||
|
return "inspect_fields";
|
||||||
|
}
|
||||||
|
if (/(?:\u0440\u0435\u0433\u0438\u0441\u0442\u0440|register)/iu.test(text)) {
|
||||||
|
return "inspect_registers";
|
||||||
|
}
|
||||||
|
if (/(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|document)/iu.test(text)) {
|
||||||
|
return "inspect_documents";
|
||||||
|
}
|
||||||
|
if (/(?:\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a|directory|catalog)/iu.test(text)) {
|
||||||
|
return "inspect_catalog";
|
||||||
|
}
|
||||||
|
return "inspect_catalog";
|
||||||
|
}
|
||||||
|
|
||||||
function hasExplicitDateScopeLiteral(text: string): boolean {
|
function hasExplicitDateScopeLiteral(text: string): boolean {
|
||||||
return /(?:\b(?:19|20)\d{2}\b|\b\d{4}-\d{2}-\d{2}\b|\b\d{4}-\d{2}\b)/iu.test(text);
|
return /(?:\b(?:19|20)\d{2}\b|\b\d{4}-\d{2}-\d{2}\b|\b\d{4}-\d{2}\b)/iu.test(text);
|
||||||
}
|
}
|
||||||
|
|
@ -332,8 +364,12 @@ function semanticNeedFor(input: {
|
||||||
unsupported: string | null;
|
unsupported: string | null;
|
||||||
lifecycleSignal: boolean;
|
lifecycleSignal: boolean;
|
||||||
valueFlowSignal: boolean;
|
valueFlowSignal: boolean;
|
||||||
|
metadataSignal: boolean;
|
||||||
}): string | null {
|
}): string | null {
|
||||||
const combined = compactLower(`${input.domain ?? ""} ${input.action ?? ""} ${input.unsupported ?? ""}`);
|
const combined = compactLower(`${input.domain ?? ""} ${input.action ?? ""} ${input.unsupported ?? ""}`);
|
||||||
|
if (input.metadataSignal || /(?:metadata|schema|catalog|inspect_(?:catalog|documents|registers|fields))/iu.test(combined)) {
|
||||||
|
return "1C metadata evidence";
|
||||||
|
}
|
||||||
if (input.lifecycleSignal || /(?:lifecycle|activity|duration|age)/iu.test(combined)) {
|
if (input.lifecycleSignal || /(?:lifecycle|activity|duration|age)/iu.test(combined)) {
|
||||||
return "counterparty lifecycle evidence";
|
return "counterparty lifecycle evidence";
|
||||||
}
|
}
|
||||||
|
|
@ -343,9 +379,6 @@ function semanticNeedFor(input: {
|
||||||
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
||||||
return "document evidence";
|
return "document evidence";
|
||||||
}
|
}
|
||||||
if (/(?:metadata|schema|catalog)/iu.test(combined)) {
|
|
||||||
return "1C metadata evidence";
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,6 +386,7 @@ function shouldRunDiscovery(input: {
|
||||||
unsupported: string | null;
|
unsupported: string | null;
|
||||||
lifecycleSignal: boolean;
|
lifecycleSignal: boolean;
|
||||||
valueFlowSignal: boolean;
|
valueFlowSignal: boolean;
|
||||||
|
metadataSignal: boolean;
|
||||||
semanticDataNeed: string | null;
|
semanticDataNeed: string | null;
|
||||||
explicitIntentCandidate: string | null;
|
explicitIntentCandidate: string | null;
|
||||||
followupDiscoverySeedApplicable: boolean;
|
followupDiscoverySeedApplicable: boolean;
|
||||||
|
|
@ -360,6 +394,9 @@ function shouldRunDiscovery(input: {
|
||||||
if (input.lifecycleSignal || input.unsupported) {
|
if (input.lifecycleSignal || input.unsupported) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (input.metadataSignal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (input.valueFlowSignal && !input.explicitIntentCandidate) {
|
if (input.valueFlowSignal && !input.explicitIntentCandidate) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -386,6 +423,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
||||||
const rawValueFlowSignal =
|
const rawValueFlowSignal =
|
||||||
!rawLifecycleSignal && (hasValueFlowSignal(rawText) || rawBidirectionalValueFlowSignal);
|
!rawLifecycleSignal && (hasValueFlowSignal(rawText) || rawBidirectionalValueFlowSignal);
|
||||||
|
const rawMetadataSignal = !rawLifecycleSignal && !rawValueFlowSignal && hasMetadataSignal(rawText);
|
||||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||||
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
||||||
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
||||||
|
|
@ -424,7 +462,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
action: rawAction ?? seededAction,
|
action: rawAction ?? seededAction,
|
||||||
unsupported: unsupported ?? seededUnsupported,
|
unsupported: unsupported ?? seededUnsupported,
|
||||||
lifecycleSignal,
|
lifecycleSignal,
|
||||||
valueFlowSignal
|
valueFlowSignal,
|
||||||
|
metadataSignal: rawMetadataSignal
|
||||||
});
|
});
|
||||||
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
||||||
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
||||||
|
|
@ -445,7 +484,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
? "counterparty_lifecycle"
|
? "counterparty_lifecycle"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
? "counterparty_value"
|
? "counterparty_value"
|
||||||
: rawDomain ?? seededDomain,
|
: rawMetadataSignal
|
||||||
|
? "metadata"
|
||||||
|
: rawDomain ?? seededDomain,
|
||||||
asked_action_family: lifecycleSignal
|
asked_action_family: lifecycleSignal
|
||||||
? "activity_duration"
|
? "activity_duration"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
|
|
@ -454,7 +495,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
: payoutSignal
|
: payoutSignal
|
||||||
? "payout"
|
? "payout"
|
||||||
: rawAction ?? seededAction ?? "turnover"
|
: rawAction ?? seededAction ?? "turnover"
|
||||||
: rawAction ?? seededAction,
|
: rawMetadataSignal
|
||||||
|
? metadataActionFromRawText(rawText)
|
||||||
|
: rawAction ?? seededAction,
|
||||||
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
||||||
explicit_entity_candidates: entityCandidates,
|
explicit_entity_candidates: entityCandidates,
|
||||||
explicit_organization_scope: explicitOrganizationScope,
|
explicit_organization_scope: explicitOrganizationScope,
|
||||||
|
|
@ -469,6 +512,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
: payoutSignal
|
: payoutSignal
|
||||||
? "counterparty_payouts_or_outflow"
|
? "counterparty_payouts_or_outflow"
|
||||||
: seededUnsupported ?? "counterparty_value_or_turnover"
|
: seededUnsupported ?? "counterparty_value_or_turnover"
|
||||||
|
: rawMetadataSignal
|
||||||
|
? "1c_metadata_surface"
|
||||||
: followupDiscoverySeedApplicable
|
: followupDiscoverySeedApplicable
|
||||||
? seededUnsupported
|
? seededUnsupported
|
||||||
: null),
|
: null),
|
||||||
|
|
@ -477,6 +522,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
unsupported ||
|
unsupported ||
|
||||||
lifecycleSignal ||
|
lifecycleSignal ||
|
||||||
valueFlowSignal ||
|
valueFlowSignal ||
|
||||||
|
rawMetadataSignal ||
|
||||||
followupDiscoverySeedApplicable
|
followupDiscoverySeedApplicable
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
@ -511,6 +557,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
unsupported: unsupported ?? seededUnsupported,
|
unsupported: unsupported ?? seededUnsupported,
|
||||||
lifecycleSignal,
|
lifecycleSignal,
|
||||||
valueFlowSignal,
|
valueFlowSignal,
|
||||||
|
metadataSignal: rawMetadataSignal,
|
||||||
semanticDataNeed,
|
semanticDataNeed,
|
||||||
explicitIntentCandidate,
|
explicitIntentCandidate,
|
||||||
followupDiscoverySeedApplicable
|
followupDiscoverySeedApplicable
|
||||||
|
|
@ -526,6 +573,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
? "raw_text"
|
? "raw_text"
|
||||||
: valueFlowSignal
|
: valueFlowSignal
|
||||||
? "raw_text"
|
? "raw_text"
|
||||||
|
: rawMetadataSignal
|
||||||
|
? "raw_text"
|
||||||
: "none";
|
: "none";
|
||||||
|
|
||||||
if (lifecycleSignal) {
|
if (lifecycleSignal) {
|
||||||
|
|
@ -534,6 +583,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
if (valueFlowSignal) {
|
if (valueFlowSignal) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_value_flow_signal_detected");
|
pushReason(reasonCodes, "mcp_discovery_value_flow_signal_detected");
|
||||||
}
|
}
|
||||||
|
if (rawMetadataSignal) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_metadata_signal_detected");
|
||||||
|
}
|
||||||
if (payoutSignal) {
|
if (payoutSignal) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,17 @@ function buildSequentialDeps(results: Array<{ rows: Array<Record<string, unknown
|
||||||
return { executeAddressMcpQuery };
|
return { executeAddressMcpQuery };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||||||
|
return {
|
||||||
|
executeAddressMcpMetadata: vi.fn(async () => ({
|
||||||
|
fetched_rows: error ? 0 : rows.length,
|
||||||
|
raw_rows: error ? [] : rows,
|
||||||
|
rows: error ? [] : rows,
|
||||||
|
error
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("assistant MCP discovery answer adapter", () => {
|
describe("assistant MCP discovery answer adapter", () => {
|
||||||
it("turns confirmed lifecycle evidence into a human-safe bounded answer draft", async () => {
|
it("turns confirmed lifecycle evidence into a human-safe bounded answer draft", async () => {
|
||||||
const planner = planAssistantMcpDiscovery({
|
const planner = planAssistantMcpDiscovery({
|
||||||
|
|
@ -96,6 +107,36 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||||
expect(draft.must_not_claim).toContain("Do not claim rows were checked when mcp_execution_performed=false.");
|
expect(draft.must_not_claim).toContain("Do not claim rows were checked when mcp_execution_performed=false.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("turns metadata surface evidence into a human-safe metadata answer draft", async () => {
|
||||||
|
const planner = planAssistantMcpDiscovery({
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "metadata",
|
||||||
|
asked_action_family: "inspect_documents",
|
||||||
|
explicit_entity_candidates: ["НДС"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||||
|
planner,
|
||||||
|
buildMetadataDeps([
|
||||||
|
{
|
||||||
|
FullName: "Документ.СчетФактураВыданный",
|
||||||
|
MetaType: "Документ",
|
||||||
|
attributes: [{ Name: "Дата" }, { Name: "Организация" }]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||||
|
const confirmedText = draft.confirmed_lines.join("\n");
|
||||||
|
|
||||||
|
expect(draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||||
|
expect(draft.headline).toContain("метаданным 1С");
|
||||||
|
expect(confirmedText).toContain("Подтвержденная metadata-поверхность 1С");
|
||||||
|
expect(confirmedText).toContain("Документ.СчетФактураВыданный");
|
||||||
|
expect(confirmedText).toContain("Дата");
|
||||||
|
expect(draft.must_not_claim).toContain("Do not present metadata surface as confirmed business data rows.");
|
||||||
|
});
|
||||||
|
|
||||||
it("turns value-flow evidence into a bounded turnover answer draft", async () => {
|
it("turns value-flow evidence into a bounded turnover answer draft", async () => {
|
||||||
const planner = planAssistantMcpDiscovery({
|
const planner = planAssistantMcpDiscovery({
|
||||||
turnMeaning: {
|
turnMeaning: {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,17 @@ function buildSequentialDeps(results: Array<{ rows: Array<Record<string, unknown
|
||||||
return { executeAddressMcpQuery };
|
return { executeAddressMcpQuery };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||||||
|
return {
|
||||||
|
executeAddressMcpMetadata: vi.fn(async () => ({
|
||||||
|
fetched_rows: error ? 0 : rows.length,
|
||||||
|
raw_rows: error ? [] : rows,
|
||||||
|
rows: error ? [] : rows,
|
||||||
|
error
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("assistant MCP discovery pilot executor", () => {
|
describe("assistant MCP discovery pilot executor", () => {
|
||||||
it("executes only the lifecycle query_documents primitive through injected MCP deps", async () => {
|
it("executes only the lifecycle query_documents primitive through injected MCP deps", async () => {
|
||||||
const planner = planAssistantMcpDiscovery({
|
const planner = planAssistantMcpDiscovery({
|
||||||
|
|
@ -92,6 +103,53 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||||
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("executes inspect_1c_metadata and derives a confirmed metadata surface", async () => {
|
||||||
|
const planner = planAssistantMcpDiscovery({
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "metadata",
|
||||||
|
asked_action_family: "inspect_documents",
|
||||||
|
explicit_entity_candidates: ["НДС"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const deps = buildMetadataDeps([
|
||||||
|
{
|
||||||
|
FullName: "Документ.СчетФактураВыданный",
|
||||||
|
MetaType: "Документ",
|
||||||
|
attributes: [{ Name: "Дата" }, { Name: "Организация" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FullName: "Документ.СчетФактураПолученный",
|
||||||
|
MetaType: "Документ",
|
||||||
|
attributes: [{ Name: "Контрагент" }]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||||
|
|
||||||
|
expect(result.pilot_status).toBe("executed");
|
||||||
|
expect(result.pilot_scope).toBe("metadata_inspection_v1");
|
||||||
|
expect(result.mcp_execution_performed).toBe(true);
|
||||||
|
expect(result.executed_primitives).toEqual(["inspect_1c_metadata"]);
|
||||||
|
expect(result.evidence.evidence_status).toBe("confirmed");
|
||||||
|
expect(result.source_rows_summary).toBe("2 MCP metadata rows fetched");
|
||||||
|
expect(result.derived_metadata_surface).toMatchObject({
|
||||||
|
metadata_scope: "НДС",
|
||||||
|
requested_meta_types: ["Документ"],
|
||||||
|
matched_rows: 2,
|
||||||
|
available_entity_sets: ["Документ"],
|
||||||
|
matched_objects: ["Документ.СчетФактураВыданный", "Документ.СчетФактураПолученный"],
|
||||||
|
available_fields: ["Дата", "Организация", "Контрагент"],
|
||||||
|
inference_basis: "confirmed_1c_metadata_surface_rows"
|
||||||
|
});
|
||||||
|
expect(result.reason_codes).toContain("pilot_inspect_1c_metadata_mcp_executed");
|
||||||
|
expect(result.reason_codes).toContain("pilot_derived_metadata_surface_from_confirmed_rows");
|
||||||
|
expect(deps.executeAddressMcpMetadata).toHaveBeenCalledTimes(1);
|
||||||
|
expect(deps.executeAddressMcpMetadata.mock.calls[0]?.[0]).toMatchObject({
|
||||||
|
meta_type: ["Документ"],
|
||||||
|
name_mask: "НДС"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("executes value-flow query_movements and derives a guarded turnover sum", async () => {
|
it("executes value-flow query_movements and derives a guarded turnover sum", async () => {
|
||||||
const planner = planAssistantMcpDiscovery({
|
const planner = planAssistantMcpDiscovery({
|
||||||
turnMeaning: {
|
turnMeaning: {
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,19 @@ describe("assistant MCP discovery planner", () => {
|
||||||
expect(result.catalog_review.evidence_floors.inspect_1c_metadata).toBe("source_summary");
|
expect(result.catalog_review.evidence_floors.inspect_1c_metadata).toBe("source_summary");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps metadata document inspection on inspect_1c_metadata instead of query_documents", () => {
|
||||||
|
const result = planAssistantMcpDiscovery({
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "metadata",
|
||||||
|
asked_action_family: "inspect_documents"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.planner_status).toBe("ready_for_execution");
|
||||||
|
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
||||||
|
expect(result.proposed_primitives).not.toContain("query_documents");
|
||||||
|
});
|
||||||
|
|
||||||
it("does not mark an unclassified turn as executable without turn meaning context", () => {
|
it("does not mark an unclassified turn as executable without turn meaning context", () => {
|
||||||
const result = planAssistantMcpDiscovery({});
|
const result = planAssistantMcpDiscovery({});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,17 @@ function buildDeps(rows: Array<Record<string, unknown>>, error: string | null =
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
|
||||||
|
return {
|
||||||
|
executeAddressMcpMetadata: vi.fn(async () => ({
|
||||||
|
fetched_rows: error ? 0 : rows.length,
|
||||||
|
raw_rows: error ? [] : rows,
|
||||||
|
rows: error ? [] : rows,
|
||||||
|
error
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("assistant MCP discovery runtime entry point", () => {
|
describe("assistant MCP discovery runtime entry point", () => {
|
||||||
it("runs the bridge for discovery-eligible lifecycle turn context", async () => {
|
it("runs the bridge for discovery-eligible lifecycle turn context", async () => {
|
||||||
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
|
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
|
||||||
|
|
@ -78,4 +89,27 @@ describe("assistant MCP discovery runtime entry point", () => {
|
||||||
expect(result.bridge?.hot_runtime_wired).toBe(false);
|
expect(result.bridge?.hot_runtime_wired).toBe(false);
|
||||||
expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn");
|
expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("runs the bridge for raw metadata wording without an exact route owner", async () => {
|
||||||
|
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
|
||||||
|
userMessage: "какие документы и поля есть в 1С по НДС?",
|
||||||
|
deps: buildMetadataDeps([
|
||||||
|
{
|
||||||
|
FullName: "Документ.СчетФактураВыданный",
|
||||||
|
MetaType: "Документ",
|
||||||
|
attributes: [{ Name: "Дата" }]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.entry_status).toBe("bridge_executed");
|
||||||
|
expect(result.discovery_attempted).toBe(true);
|
||||||
|
expect(result.turn_input.semantic_data_need).toBe("1C metadata evidence");
|
||||||
|
expect(result.turn_input.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "metadata",
|
||||||
|
asked_action_family: "inspect_fields"
|
||||||
|
});
|
||||||
|
expect(result.bridge?.pilot.pilot_scope).toBe("metadata_inspection_v1");
|
||||||
|
expect(result.bridge?.answer_draft.headline).toContain("метаданным 1С");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,24 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||||
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("bootstraps metadata discovery from raw schema wording", () => {
|
||||||
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
userMessage: "какие регистры и поля есть в 1С по НДС?"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.adapter_status).toBe("ready");
|
||||||
|
expect(result.should_run_discovery).toBe(true);
|
||||||
|
expect(result.source_signal).toBe("raw_text");
|
||||||
|
expect(result.semantic_data_need).toBe("1C metadata evidence");
|
||||||
|
expect(result.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "metadata",
|
||||||
|
asked_action_family: "inspect_fields",
|
||||||
|
unsupported_but_understood_family: "1c_metadata_surface",
|
||||||
|
stale_replay_forbidden: true
|
||||||
|
});
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_metadata_signal_detected");
|
||||||
|
});
|
||||||
|
|
||||||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
userMessage: "а по месяцам?",
|
userMessage: "а по месяцам?",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue