Planner Autonomy: связать inventory templates с exact runtime
This commit is contained in:
@@ -391,7 +391,13 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
||||
if (isEntityResolutionPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По каталогу 1С найден вероятный контрагент; это заземление сущности для следующего шага, а не еще бизнес-ответ по данным.";
|
||||
}
|
||||
if (isInventoryTemplatePilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По exact inventory runtime в 1С найдены подтвержденные строки; ответ ограничен проверенным складским/товарным срезом.";
|
||||
}
|
||||
if (isInventoryTemplatePilot(pilot) && mode === "checked_sources_only") {
|
||||
if (pilot.mcp_execution_performed) {
|
||||
return "Exact inventory runtime был проверен, но подтвержденный складской/товарный факт в найденных строках не получен.";
|
||||
}
|
||||
return "Инвентарный route-template уже выбран, но live-исполнение этого generic MCP контура еще не подключено; складской/товарный факт не подтвержден.";
|
||||
}
|
||||
if (isEntityResolutionPilot(pilot) && mode === "needs_clarification") {
|
||||
@@ -532,6 +538,9 @@ function nextStepFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
||||
return "Уточните контрагента, период или организацию, и я смогу выполнить проверку по 1С.";
|
||||
}
|
||||
if (mode === "checked_sources_only" && isInventoryTemplatePilot(pilot)) {
|
||||
if (pilot.mcp_execution_performed) {
|
||||
return "Можно уточнить дату, организацию, склад, поставщика или позицию и повторить exact inventory проверку.";
|
||||
}
|
||||
return "Следующий шаг - связать inventory route-template с exact inventory runtime и затем проверить live-прогоном.";
|
||||
}
|
||||
if (mode === "confirmed_with_bounded_inference" && pilot.derived_metadata_surface) {
|
||||
@@ -589,8 +598,11 @@ function buildMustNotClaim(pilot: AssistantMcpDiscoveryPilotExecutionContract):
|
||||
claims.push("Do not imply that the resolved entity has already been used in a downstream data probe.");
|
||||
}
|
||||
if (isInventoryTemplatePilot(pilot)) {
|
||||
claims.push("Do not present inventory route-template planning as executed stock, supplier, purchase, or sale evidence.");
|
||||
if (!pilot.mcp_execution_performed) {
|
||||
claims.push("Do not present inventory route-template planning as executed stock, supplier, purchase, or sale evidence.");
|
||||
}
|
||||
claims.push("Do not expose inventory_route_template_v1 or MCP primitive names in the user answer.");
|
||||
claims.push("Do not claim full inventory coverage outside the checked rows, date, organization, item, or supplier scope.");
|
||||
}
|
||||
if (pilot.evidence.confirmed_facts.length === 0) {
|
||||
claims.push("Do not claim a confirmed business fact when confirmed_facts is empty.");
|
||||
|
||||
@@ -337,6 +337,27 @@ function dateScopeToFilters(dateScope: string | null): Pick<AddressFilterSet, "p
|
||||
return {};
|
||||
}
|
||||
|
||||
function asOfDateFromDateScope(dateScope: string | null): string | null {
|
||||
if (!dateScope) {
|
||||
return null;
|
||||
}
|
||||
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||
if (dateMatch) {
|
||||
return `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
||||
}
|
||||
const monthMatch = dateScope.match(/^(\d{4})-(\d{2})$/);
|
||||
if (monthMatch) {
|
||||
const year = Number(monthMatch[1]);
|
||||
const month = Number(monthMatch[2]);
|
||||
if (Number.isFinite(year) && Number.isFinite(month) && month >= 1 && month <= 12) {
|
||||
const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate();
|
||||
return `${monthMatch[1]}-${monthMatch[2]}-${String(lastDay).padStart(2, "0")}`;
|
||||
}
|
||||
}
|
||||
const yearMatch = dateScope.match(/^(\d{4})$/);
|
||||
return yearMatch ? `${yearMatch[1]}-12-31` : null;
|
||||
}
|
||||
|
||||
function buildLifecycleFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const counterparty = firstEntityCandidate(planner);
|
||||
@@ -365,6 +386,37 @@ function buildValueFlowFilters(planner: AssistantMcpDiscoveryPlannerContract): A
|
||||
};
|
||||
}
|
||||
|
||||
function buildInventoryExactFilters(planner: AssistantMcpDiscoveryPlannerContract): AddressFilterSet {
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const subject = firstEntityCandidate(planner);
|
||||
const organization = toNonEmptyString(meaning?.explicit_organization_scope);
|
||||
const dateScope = toNonEmptyString(meaning?.explicit_date_scope);
|
||||
const asOfDate = asOfDateFromDateScope(dateScope);
|
||||
const filters: AddressFilterSet = {
|
||||
...dateScopeToFilters(dateScope),
|
||||
...(asOfDate ? { as_of_date: asOfDate } : {}),
|
||||
...(organization ? { organization } : {}),
|
||||
limit: planner.discovery_plan.execution_budget.max_rows_per_probe,
|
||||
sort: "period_asc"
|
||||
};
|
||||
if (
|
||||
planner.selected_chain_id === "inventory_purchase_provenance" ||
|
||||
planner.selected_chain_id === "inventory_sale_trace"
|
||||
) {
|
||||
return {
|
||||
...filters,
|
||||
...(subject ? { item: subject } : {})
|
||||
};
|
||||
}
|
||||
if (planner.selected_chain_id === "inventory_supplier_overlap") {
|
||||
return {
|
||||
...filters,
|
||||
...(subject ? { counterparty: subject } : {})
|
||||
};
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
function organizationScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string | null {
|
||||
return toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_organization_scope);
|
||||
}
|
||||
@@ -611,6 +663,15 @@ function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract)
|
||||
);
|
||||
}
|
||||
|
||||
function isInventoryPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
return (
|
||||
planner.selected_chain_id === "inventory_stock_snapshot" ||
|
||||
planner.selected_chain_id === "inventory_supplier_overlap" ||
|
||||
planner.selected_chain_id === "inventory_purchase_provenance" ||
|
||||
planner.selected_chain_id === "inventory_sale_trace"
|
||||
);
|
||||
}
|
||||
|
||||
function isMetadataPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (
|
||||
planner.selected_chain_id === "metadata_inspection" ||
|
||||
@@ -766,6 +827,36 @@ function valueFlowPilotProfile(planner: AssistantMcpDiscoveryPlannerContract): V
|
||||
};
|
||||
}
|
||||
|
||||
function inventoryIntentForPlanner(planner: AssistantMcpDiscoveryPlannerContract): AddressIntent | null {
|
||||
switch (planner.selected_chain_id) {
|
||||
case "inventory_stock_snapshot":
|
||||
return "inventory_on_hand_as_of_date";
|
||||
case "inventory_supplier_overlap":
|
||||
return "inventory_supplier_stock_overlap_as_of_date";
|
||||
case "inventory_purchase_provenance":
|
||||
return "inventory_purchase_provenance_for_item";
|
||||
case "inventory_sale_trace":
|
||||
return "inventory_sale_trace_for_item";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function inventoryExecutablePrimitiveForPlanner(
|
||||
planner: AssistantMcpDiscoveryPlannerContract
|
||||
): AssistantMcpDiscoveryRuntimeStepContract["primitive_id"] | null {
|
||||
switch (planner.selected_chain_id) {
|
||||
case "inventory_stock_snapshot":
|
||||
case "inventory_supplier_overlap":
|
||||
return "query_movements";
|
||||
case "inventory_purchase_provenance":
|
||||
case "inventory_sale_trace":
|
||||
return "query_documents";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function skippedProbeResult(step: AssistantMcpDiscoveryRuntimeStepContract, limitation: string): AssistantMcpDiscoveryProbeResult {
|
||||
return {
|
||||
primitive_id: step.primitive_id,
|
||||
@@ -1032,6 +1123,16 @@ function summarizeValueFlowRows(result: AssistantMcpDiscoveryCoverageAwareQueryR
|
||||
return `${result.fetched_rows} MCP value-flow rows fetched, ${result.matched_rows} matched value-flow scope`;
|
||||
}
|
||||
|
||||
function summarizeInventoryRows(result: AddressMcpQueryExecutorResult): string | null {
|
||||
if (result.error) {
|
||||
return null;
|
||||
}
|
||||
if (result.fetched_rows <= 0) {
|
||||
return "0 MCP inventory exact rows fetched";
|
||||
}
|
||||
return `${result.fetched_rows} MCP inventory exact rows fetched, ${result.matched_rows} matched inventory scope`;
|
||||
}
|
||||
|
||||
function summarizeMetadataRows(result: AddressMcpMetadataRowsResult): string | null {
|
||||
if (result.error) {
|
||||
return null;
|
||||
@@ -1741,6 +1842,55 @@ function rowAmountValue(row: Record<string, unknown>): number | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function rowNumberValue(row: Record<string, unknown>, keys: string[]): number | null {
|
||||
for (const key of keys) {
|
||||
const candidate = row[key];
|
||||
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
const text = toNonEmptyString(candidate);
|
||||
if (!text) {
|
||||
continue;
|
||||
}
|
||||
const normalized = text
|
||||
.replace(/\s+/g, "")
|
||||
.replace(/\u00a0/g, "")
|
||||
.replace(",", ".")
|
||||
.replace(/[^\d.-]/g, "");
|
||||
const parsed = Number(normalized);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function rowTextValue(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 rowInventoryItemValue(row: Record<string, unknown>): string | null {
|
||||
return rowTextValue(row, ["Номенклатура", "Item", "item", "Товар", "Product", "product"]);
|
||||
}
|
||||
|
||||
function rowWarehouseValue(row: Record<string, unknown>): string | null {
|
||||
return rowTextValue(row, ["Склад", "Warehouse", "warehouse"]);
|
||||
}
|
||||
|
||||
function rowDocumentValue(row: Record<string, unknown>): string | null {
|
||||
return rowTextValue(row, ["Регистратор", "Registrator", "registrator", "Документ", "Document", "document"]);
|
||||
}
|
||||
|
||||
function rowQuantityValue(row: Record<string, unknown>): number | null {
|
||||
return rowNumberValue(row, ["Количество", "Quantity", "quantity", "Qty", "qty", "Остаток", "Balance", "balance"]);
|
||||
}
|
||||
|
||||
function rowCounterpartyValue(row: Record<string, unknown>): string | null {
|
||||
const candidates = [row["Контрагент"], row["Counterparty"], row["counterparty"], row["Наименование"], row["name"]];
|
||||
for (const candidate of candidates) {
|
||||
@@ -2219,6 +2369,130 @@ function buildBidirectionalValueFlowConfirmedFacts(
|
||||
];
|
||||
}
|
||||
|
||||
function inventoryLabelRu(intent: AddressIntent): string {
|
||||
if (intent === "inventory_supplier_stock_overlap_as_of_date") {
|
||||
return "связи поставщиков с товарным остатком";
|
||||
}
|
||||
if (intent === "inventory_purchase_provenance_for_item") {
|
||||
return "закупочной истории позиции";
|
||||
}
|
||||
if (intent === "inventory_sale_trace_for_item") {
|
||||
return "продаж по позиции";
|
||||
}
|
||||
return "складского среза";
|
||||
}
|
||||
|
||||
function inventoryScopeSuffixRu(input: {
|
||||
intent: AddressIntent;
|
||||
item: string | null;
|
||||
counterparty: string | null;
|
||||
organization: string | null;
|
||||
asOfDate: string | null;
|
||||
dateScope: string | null;
|
||||
}): string {
|
||||
const parts: string[] = [];
|
||||
if (input.organization) {
|
||||
parts.push(`по организации ${input.organization}`);
|
||||
}
|
||||
if (input.item) {
|
||||
parts.push(`по позиции ${input.item}`);
|
||||
}
|
||||
if (input.intent === "inventory_supplier_stock_overlap_as_of_date" && input.counterparty) {
|
||||
parts.push(`по поставщику/контрагенту ${input.counterparty}`);
|
||||
}
|
||||
if (input.asOfDate) {
|
||||
parts.push(`на ${input.asOfDate}`);
|
||||
} else if (input.dateScope) {
|
||||
parts.push(`за ${input.dateScope}`);
|
||||
}
|
||||
return parts.length > 0 ? ` ${parts.join(", ")}` : "";
|
||||
}
|
||||
|
||||
function inventoryRowSample(row: Record<string, unknown>): string | null {
|
||||
const item = rowInventoryItemValue(row);
|
||||
const quantity = rowQuantityValue(row);
|
||||
const warehouse = rowWarehouseValue(row);
|
||||
const counterparty = rowCounterpartyValue(row);
|
||||
const document = rowDocumentValue(row);
|
||||
const parts: string[] = [];
|
||||
if (item) {
|
||||
parts.push(item);
|
||||
}
|
||||
if (quantity !== null) {
|
||||
parts.push(`${quantity} шт.`);
|
||||
}
|
||||
if (warehouse) {
|
||||
parts.push(`склад ${warehouse}`);
|
||||
}
|
||||
if (counterparty) {
|
||||
parts.push(`контрагент ${counterparty}`);
|
||||
}
|
||||
if (document) {
|
||||
parts.push(`документ ${document}`);
|
||||
}
|
||||
return parts.length > 0 ? parts.join(", ") : null;
|
||||
}
|
||||
|
||||
function buildInventoryConfirmedFacts(
|
||||
result: AddressMcpQueryExecutorResult,
|
||||
planner: AssistantMcpDiscoveryPlannerContract,
|
||||
intent: AddressIntent
|
||||
): string[] {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
||||
const item =
|
||||
intent === "inventory_purchase_provenance_for_item" || intent === "inventory_sale_trace_for_item"
|
||||
? firstEntityCandidate(planner)
|
||||
: null;
|
||||
const counterparty = intent === "inventory_supplier_stock_overlap_as_of_date" ? firstEntityCandidate(planner) : null;
|
||||
const scope = inventoryScopeSuffixRu({
|
||||
intent,
|
||||
item,
|
||||
counterparty,
|
||||
organization: organizationScopeForPlanner(planner),
|
||||
asOfDate: asOfDateFromDateScope(dateScope),
|
||||
dateScope
|
||||
});
|
||||
const samples = result.rows
|
||||
.slice(0, 3)
|
||||
.map((row) => inventoryRowSample(row))
|
||||
.filter((value): value is string => Boolean(value));
|
||||
const sampleSuffix = samples.length > 0 ? ` Примеры строк: ${samples.join("; ")}.` : "";
|
||||
return [`В 1С найдены подтвержденные строки ${inventoryLabelRu(intent)}${scope}: ${result.matched_rows}.${sampleSuffix}`];
|
||||
}
|
||||
|
||||
function buildInventoryInferredFacts(result: AddressMcpQueryExecutorResult, intent: AddressIntent): string[] {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
if (result.matched_rows <= 0) {
|
||||
return [
|
||||
`По ${inventoryLabelRu(intent)} удалось проверить только ограниченный срез 1С; подтвержденных строк этим поиском не найдено.`
|
||||
];
|
||||
}
|
||||
return [
|
||||
`Вывод по ${inventoryLabelRu(intent)} ограничен найденными строками 1С и указанными датой, организацией, позицией или поставщиком.`
|
||||
];
|
||||
}
|
||||
|
||||
function buildInventoryUnknownFacts(
|
||||
result: AddressMcpQueryExecutorResult | null,
|
||||
intent: AddressIntent,
|
||||
dateScope: string | null
|
||||
): string[] {
|
||||
const facts = [
|
||||
dateScope
|
||||
? `Полный товарный контур вне проверенного среза ${dateScope} не подтвержден.`
|
||||
: "Полный товарный контур без явного проверенного периода или даты не подтвержден."
|
||||
];
|
||||
if (!result || result.error || result.matched_rows <= 0) {
|
||||
facts.unshift(`Подтвержденный факт по ${inventoryLabelRu(intent)} в проверенных строках 1С не найден.`);
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
|
||||
function buildLifecycleInferredFacts(result: AddressMcpQueryExecutorResult): string[] {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
@@ -2441,18 +2715,6 @@ function pilotScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): As
|
||||
}
|
||||
}
|
||||
|
||||
function isLivePilotChainSupported(chainId: AssistantMcpDiscoveryChainId): boolean {
|
||||
if (
|
||||
chainId === "inventory_stock_snapshot" ||
|
||||
chainId === "inventory_supplier_overlap" ||
|
||||
chainId === "inventory_purchase_provenance" ||
|
||||
chainId === "inventory_sale_trace"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function executeAssistantMcpDiscoveryPilot(
|
||||
planner: AssistantMcpDiscoveryPlannerContract,
|
||||
deps: AssistantMcpDiscoveryPilotExecutorDeps = DEFAULT_DEPS
|
||||
@@ -2525,16 +2787,16 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||
const entityResolutionPilotEligible = isEntityResolutionPilotEligible(planner);
|
||||
const livePilotChainSupported = isLivePilotChainSupported(planner.selected_chain_id);
|
||||
const inventoryPilotEligible = isInventoryPilotEligible(planner);
|
||||
|
||||
if (
|
||||
!livePilotChainSupported ||
|
||||
(!metadataPilotEligible &&
|
||||
!documentPilotEligible &&
|
||||
!movementPilotEligible &&
|
||||
!lifecyclePilotEligible &&
|
||||
!valueFlowPilotEligible &&
|
||||
!entityResolutionPilotEligible)
|
||||
!metadataPilotEligible &&
|
||||
!documentPilotEligible &&
|
||||
!movementPilotEligible &&
|
||||
!lifecyclePilotEligible &&
|
||||
!valueFlowPilotEligible &&
|
||||
!entityResolutionPilotEligible &&
|
||||
!inventoryPilotEligible
|
||||
) {
|
||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||
for (const step of dryRun.execution_steps) {
|
||||
@@ -2570,6 +2832,152 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
const aggregationAxis = aggregationAxisForPlanner(planner);
|
||||
const rankingNeed = rankingNeedForPlanner(planner);
|
||||
|
||||
if (inventoryPilotEligible) {
|
||||
let queryResult: AddressMcpQueryExecutorResult | null = null;
|
||||
const inventoryIntent = inventoryIntentForPlanner(planner);
|
||||
const executablePrimitive = inventoryExecutablePrimitiveForPlanner(planner);
|
||||
if (!inventoryIntent || !executablePrimitive) {
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_recipe_not_mapped");
|
||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Inventory exact recipe is not mapped");
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "unsupported",
|
||||
pilot_scope: "inventory_route_template_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: ["Inventory exact recipe is not mapped"],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
const filters = buildInventoryExactFilters(planner);
|
||||
const selection = selectAddressRecipe(inventoryIntent, filters);
|
||||
if (selection.missing_required_filters.length > 0) {
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_recipe_needs_required_filters");
|
||||
const evidence = buildEmptyEvidence(
|
||||
planner,
|
||||
dryRun,
|
||||
probeResults,
|
||||
`Inventory exact recipe needs required filters: ${selection.missing_required_filters.join(", ")}`
|
||||
);
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "skipped_needs_clarification",
|
||||
pilot_scope: "inventory_route_template_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: [`Inventory exact recipe needs required filters: ${selection.missing_required_filters.join(", ")}`],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
if (!selection.selected_recipe) {
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_recipe_not_available");
|
||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Inventory exact recipe is not available");
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "unsupported",
|
||||
pilot_scope: "inventory_route_template_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: ["Inventory exact recipe is not available"],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_recipe_selected");
|
||||
const recipePlan = buildAddressRecipePlan(selection.selected_recipe, filters);
|
||||
for (const step of dryRun.execution_steps) {
|
||||
if (step.primitive_id !== executablePrimitive) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, `pilot_inventory_exact_bridge_executes_${executablePrimitive}`));
|
||||
continue;
|
||||
}
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: recipePlan.query,
|
||||
limit: recipePlan.limit,
|
||||
account_scope: recipePlan.account_scope
|
||||
});
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_mcp_error");
|
||||
} else {
|
||||
pushReason(reasonCodes, "pilot_inventory_exact_mcp_executed");
|
||||
}
|
||||
}
|
||||
|
||||
const sourceRowsSummary = queryResult ? summarizeInventoryRows(queryResult) : null;
|
||||
const evidence = resolveAssistantMcpDiscoveryEvidence({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: queryResult ? buildInventoryConfirmedFacts(queryResult, planner, inventoryIntent) : [],
|
||||
inferredFacts: queryResult ? buildInventoryInferredFacts(queryResult, inventoryIntent) : [],
|
||||
unknownFacts: buildInventoryUnknownFacts(
|
||||
queryResult,
|
||||
inventoryIntent,
|
||||
toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope)
|
||||
),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
});
|
||||
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "executed",
|
||||
pilot_scope: "inventory_route_template_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: executedPrimitives.length > 0,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
if (metadataPilotEligible) {
|
||||
let metadataResult: AddressMcpMetadataRowsResult | null = null;
|
||||
const metadataScope = metadataScopeForPlanner(planner);
|
||||
|
||||
Reference in New Issue
Block a user