ARCH: замкнуть grounded entity follow-up на документы и денежный поток

This commit is contained in:
2026-04-22 13:57:57 +03:00
parent ce48fa83a5
commit 1fd8062dc7
20 changed files with 1092 additions and 142 deletions
@@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readAssistantMcpDiscoveryEntityCandidates = readAssistantMcpDiscoveryEntityCandidates;
exports.readAssistantMcpDiscoveryPilotScope = readAssistantMcpDiscoveryPilotScope;
exports.readAssistantMcpDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily;
exports.readAssistantMcpDiscoveryMetadataSelectedEntitySet = readAssistantMcpDiscoveryMetadataSelectedEntitySet;
@@ -91,6 +92,39 @@ function readAssistantMcpDiscoveryDerivedMetadataSurface(debug) {
const pilot = toRecordObject(bridge?.pilot);
return toRecordObject(pilot?.derived_metadata_surface);
}
function readAssistantMcpDiscoveryDerivedEntityResolution(debug) {
const bridge = readAssistantMcpDiscoveryBridge(debug);
const pilot = toRecordObject(bridge?.pilot);
return toRecordObject(pilot?.derived_entity_resolution);
}
function collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString = fallbackToNonEmptyString) {
const result = [];
const resolution = readAssistantMcpDiscoveryDerivedEntityResolution(debug);
const pushCandidate = (value) => {
const text = toNonEmptyString(value);
if (text && !result.includes(text)) {
result.push(text);
}
};
pushCandidate(resolution?.resolved_entity);
pushCandidate(resolution?.requested_entity);
if (Array.isArray(resolution?.ambiguity_candidates)) {
for (const candidate of resolution.ambiguity_candidates) {
pushCandidate(candidate);
}
}
const discoveryMeaning = readAssistantMcpDiscoveryTurnMeaning(debug);
const explicitEntities = Array.isArray(discoveryMeaning?.explicit_entity_candidates)
? discoveryMeaning.explicit_entity_candidates
: [];
for (const entity of explicitEntities) {
pushCandidate(candidateValue(entity));
}
return result;
}
function readAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString = fallbackToNonEmptyString) {
return collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString);
}
function readAssistantMcpDiscoveryPilotScope(debug, toNonEmptyString = fallbackToNonEmptyString) {
const bridge = readAssistantMcpDiscoveryBridge(debug);
const pilot = toRecordObject(bridge?.pilot);
@@ -250,12 +284,9 @@ function readAddressDebugCounterparty(debug, toNonEmptyString = fallbackToNonEmp
if (String(debug?.anchor_type ?? "") === "counterparty") {
return toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw);
}
const discoveryMeaning = readAssistantMcpDiscoveryTurnMeaning(debug);
const explicitEntities = Array.isArray(discoveryMeaning?.explicit_entity_candidates)
? discoveryMeaning?.explicit_entity_candidates
: [];
for (const entity of explicitEntities) {
const text = candidateValue(entity);
const discoveryEntities = collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString);
for (const entity of discoveryEntities) {
const text = toNonEmptyString(entity);
if (text) {
return text;
}
@@ -113,6 +113,21 @@ function firstEntityCandidate(pilot) {
}
return null;
}
function explicitDateScope(pilot) {
const value = pilot.evidence.query_plan.turn_meaning_ref?.explicit_date_scope;
if (typeof value !== "string") {
return null;
}
const normalized = value.trim();
return normalized.length > 0 ? normalized : null;
}
function documentOrMovementScopeRu(pilot) {
const entity = firstEntityCandidate(pilot);
const period = explicitDateScope(pilot);
const entityPart = entity ? ` по контрагенту ${entity}` : "";
const periodPart = period ? ` за ${period}` : " в проверенном окне";
return `${entityPart}${periodPart}`;
}
function isMovementLaneClarification(pilot) {
return (isMovementPilot(pilot) ||
pilot.reason_codes.includes("planner_selected_movement_recipe") ||
@@ -190,7 +205,7 @@ function headlineFor(mode, pilot) {
return "По текущему каталожному поиску 1С точный контрагент пока не подтвержден.";
}
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
return "По данным 1С найдены строки движений; ответ ограничен проверенным периодом и найденными строками.";
return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
}
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
if (pilot.derived_metadata_surface.ambiguity_detected) {
@@ -208,7 +223,7 @@ function headlineFor(mode, pilot) {
if (pilot.derived_value_flow.value_flow_direction === "outgoing_supplier_payout") {
return "По данным 1С найдены строки исходящих платежей/списаний; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
}
return "По данным 1С найдены строки денежных движений; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
return "По данным 1С найдены строки входящих денежных поступлений; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
}
if (pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто можно называть только как расчет по найденным строкам и проверенному периоду.";
@@ -217,14 +232,23 @@ function headlineFor(mode, pilot) {
if (pilot.derived_value_flow.value_flow_direction === "outgoing_supplier_payout") {
return "По данным 1С найдены строки исходящих платежей/списаний; сумму можно называть только в рамках проверенного периода и найденных строк.";
}
return "По данным 1С найдены строки денежных движений; сумму можно называть только в рамках проверенного периода и найденных строк.";
return "По данным 1С найдены строки входящих денежных поступлений; сумму можно называть только в рамках проверенного периода и найденных строк.";
}
if (isDocumentPilot(pilot) && mode === "confirmed_with_bounded_inference") {
return "По данным 1С найдены строки документов; ответ ограничен проверенным периодом и найденными строками.";
return `По документам${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
}
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
}
if (mode === "confirmed_with_bounded_inference") {
return "По данным 1С есть подтвержденная активность; длительность можно оценивать только как вывод из этих строк.";
}
if (isDocumentPilot(pilot) && mode === "bounded_inference_only") {
return `По документам${documentOrMovementScopeRu(pilot)} полный срез не подтвержден; пока есть только ограниченная граница проверенного окна 1С.`;
}
if (isMovementPilot(pilot) && mode === "bounded_inference_only") {
return `По движениям${documentOrMovementScopeRu(pilot)} полный срез не подтвержден; пока есть только ограниченная граница проверенного окна 1С.`;
}
if (mode === "bounded_inference_only") {
return "Точный факт не подтвержден, но есть ограниченная оценка по найденной активности в 1С.";
}
@@ -436,13 +460,15 @@ function derivedValueFlowConfirmedLine(pilot) {
}
const counterparty = flow.counterparty ? ` по контрагенту ${flow.counterparty}` : "";
const period = flow.period_scope ? ` за период ${flow.period_scope}` : " в проверенном окне";
const movementLabel = flow.value_flow_direction === "outgoing_supplier_payout" ? "исходящих платежей/списаний" : "денежных движений";
const movementLabel = flow.value_flow_direction === "outgoing_supplier_payout"
? "исходящих платежей/списаний"
: "входящих денежных поступлений";
const totalLabel = flow.value_flow_direction === "outgoing_supplier_payout"
? "сумма исходящих платежей/списаний составляет"
: "сумма составляет";
: "сумма входящих денежных поступлений составляет";
const caveat = flow.value_flow_direction === "outgoing_supplier_payout"
? "Это расчет по найденным строкам 1С, а не подтверждение полного объема платежей вне проверенного окна."
: "Это расчет по найденным строкам 1С, а не подтверждение полного оборота вне проверенного окна.";
: "Это расчет по найденным строкам 1С, а не подтверждение полного объема поступлений вне проверенного окна.";
const dates = flow.first_movement_date && flow.latest_movement_date
? ` Первая найденная дата движения: ${flow.first_movement_date}; последняя: ${flow.latest_movement_date}.`
: "";
@@ -1267,25 +1267,26 @@ function buildLifecycleConfirmedFacts(result, counterparty) {
: "1C activity rows were found for the requested counterparty scope"
];
}
function buildDocumentConfirmedFacts(result, counterparty) {
if (result.error || result.matched_rows <= 0) {
return [];
}
return [
counterparty
? `1C document rows were found for counterparty ${counterparty}`
: "1C document rows were found for the requested scope"
];
function checkedCounterpartySuffixRu(counterparty) {
return counterparty ? ` по контрагенту ${counterparty}` : "";
}
function buildMovementConfirmedFacts(result, counterparty) {
function checkedPeriodSuffixRu(periodScope) {
return periodScope ? ` за ${periodScope}` : " в проверенном окне";
}
function uncheckedPeriodBoundaryRu(periodScope) {
return periodScope ? ` вне периода ${periodScope}` : " без явно проверенного периода";
}
function buildDocumentConfirmedFacts(result, counterparty, periodScope) {
if (result.error || result.matched_rows <= 0) {
return [];
}
return [
counterparty
? `1C movement rows were found for counterparty ${counterparty}`
: "1C movement rows were found for the requested scope"
];
return [`В 1С найдены строки документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
}
function buildMovementConfirmedFacts(result, counterparty, periodScope) {
if (result.error || result.matched_rows <= 0) {
return [];
}
return [`В 1С найдены строки движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
}
function buildValueFlowConfirmedFacts(result, counterparty, direction) {
if (result.error || result.matched_rows <= 0) {
@@ -1325,17 +1326,31 @@ function buildLifecycleInferredFacts(result) {
}
return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"];
}
function buildDocumentInferredFacts(result) {
function buildDocumentInferredFacts(result, counterparty, periodScope) {
if (result.error || result.fetched_rows <= 0) {
return [];
}
return ["Counterparty document evidence is limited to confirmed 1C document rows in the checked scope"];
if (result.matched_rows <= 0) {
return [
`По документам${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк документов этим поиском не найдено.`
];
}
return [
`Срез документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками документов, найденными этим поиском.`
];
}
function buildMovementInferredFacts(result) {
function buildMovementInferredFacts(result, counterparty, periodScope) {
if (result.error || result.fetched_rows <= 0) {
return [];
}
return ["Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"];
if (result.matched_rows <= 0) {
return [
`По движениям${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк движений этим поиском не найдено.`
];
}
return [
`Срез движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками движений, найденными этим поиском.`
];
}
function buildValueFlowInferredFacts(derived) {
if (!derived) {
@@ -1372,18 +1387,14 @@ function buildBidirectionalValueFlowInferredFacts(derived) {
function buildLifecycleUnknownFacts() {
return ["Legal registration date is not proven by this MCP discovery pilot"];
}
function buildDocumentUnknownFacts(periodScope) {
function buildDocumentUnknownFacts(periodScope, counterparty) {
return [
periodScope
? "Full document history outside the checked period is not proven by this MCP discovery pilot"
: "Full document history is not proven without an explicit checked period"
`Полный исторический срез документов${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
];
}
function buildMovementUnknownFacts(periodScope) {
function buildMovementUnknownFacts(periodScope, counterparty) {
return [
periodScope
? "Full movement history outside the checked period is not proven by this MCP discovery pilot"
: "Full movement history is not proven without an explicit checked period"
`Полный исторический срез движений${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
];
}
function buildValueFlowUnknownFacts(periodScope, direction, derived) {
@@ -1749,9 +1760,9 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
plan: planner.discovery_plan,
probeResults,
confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty) : [],
inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult) : [],
unknownFacts: buildDocumentUnknownFacts(dateScope),
confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty, dateScope) : [],
inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult, counterparty, dateScope) : [],
unknownFacts: buildDocumentUnknownFacts(dateScope, counterparty),
sourceRowsSummary,
queryLimitations,
recommendedNextProbe: "explain_evidence_basis"
@@ -1831,9 +1842,9 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
plan: planner.discovery_plan,
probeResults,
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty) : [],
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult) : [],
unknownFacts: buildMovementUnknownFacts(dateScope),
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty, dateScope) : [],
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult, counterparty, dateScope) : [],
unknownFacts: buildMovementUnknownFacts(dateScope, counterparty),
sourceRowsSummary,
queryLimitations,
recommendedNextProbe: "explain_evidence_basis"
@@ -70,10 +70,10 @@ function localizeLine(value) {
}
const valueFlowMatch = value.match(/^1C value-flow rows were found for counterparty\s+(.+)$/i);
if (valueFlowMatch) {
return `В 1С найдены строки денежных движений по контрагенту ${valueFlowMatch[1]}.`;
return `В 1С найдены строки входящих денежных поступлений по контрагенту ${valueFlowMatch[1]}.`;
}
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
return "В 1С найдены строки денежных движений по запрошенному контрагентскому контуру.";
return "В 1С найдены строки входящих денежных поступлений по запрошенному контрагентскому контуру.";
}
const documentRowsMatch = value.match(/^1C document rows were found for counterparty\s+(.+)$/i);
if (documentRowsMatch) {
@@ -118,10 +118,10 @@ function localizeLine(value) {
return "Срез движений ограничен только подтвержденными строками движений в проверенном окне.";
}
if (/^Counterparty value-flow total was calculated from confirmed 1C movement rows$/i.test(value)) {
return "Сумма рассчитана только по подтвержденным строкам денежных движений в 1С.";
return "Сумма входящих поступлений рассчитана только по подтвержденным строкам поступлений в 1С.";
}
if (/^Counterparty monthly value-flow breakdown was grouped by month over confirmed 1C movement rows$/i.test(value)) {
return "Помесячная раскладка денежного потока сгруппирована только по подтвержденным строкам движений 1С.";
return "Помесячная раскладка входящих поступлений построена только по подтвержденным строкам поступлений в 1С.";
}
if (/^Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows$/i.test(value)) {
return "Сумма исходящих платежей рассчитана только по подтвержденным строкам списаний в 1С.";
@@ -186,10 +186,10 @@ function localizeLine(value) {
return "Полное покрытие запрошенного периода по двустороннему денежному потоку не подтверждено: хотя бы одна сторона проверки достигла лимита найденных строк.";
}
if (/^Full turnover outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
return "Полный оборот вне проверенного периода этим поиском не подтвержден.";
return "Полный объем входящих поступлений вне проверенного периода этим поиском не подтвержден.";
}
if (/^Full all-time turnover is not proven without an explicit checked period$/i.test(value)) {
return "Полный оборот за все время без явно проверенного периода не подтвержден.";
return "Полный объем входящих поступлений за все время без явно проверенного периода не подтвержден.";
}
if (/^Full document history outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
return "Полный исторический срез документов вне проверенного периода этим поиском не подтвержден.";
@@ -36,6 +36,19 @@ function pushUnique(target, value) {
target.push(text);
}
}
function isReferentialEntityPlaceholder(value) {
return /^(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u043d\u0438\u043c|\u043d\u0438\u043c\u0438|\u0435\u0433\u043e|\u0435\u0435|\u0435\u0451|\u0438\u0445|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439|\u044d\u0442\u0438\u043c|\u044d\u0442\u0438\u043c\u0438|\u044d\u0442\u043e\u043c)$/iu.test(value.trim());
}
function pushScopedEntityCandidate(target, value, groundedFollowupEntity) {
const text = candidateValue(value);
if (!text) {
return;
}
if (groundedFollowupEntity && isReferentialEntityPlaceholder(text)) {
return;
}
pushUnique(target, text);
}
function canonicalizeEntityResolutionCandidate(value) {
return normalizeEntityResolutionCandidate(value)
.replace(/^(?:\u0441\s+\u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c\s+)/iu, "")
@@ -143,6 +156,13 @@ function mapPilotScopeToFollowupMeaning(pilotScope) {
unsupported: "movement_evidence"
};
}
if (pilotScope === "counterparty_document_evidence_query_documents_v1") {
return {
domain: "documents",
action: "list_documents",
unsupported: "document_evidence"
};
}
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
return {
domain: "counterparty_value",
@@ -225,7 +245,8 @@ function collectFollowupDiscoverySeed(followupContext) {
toNonEmptyString(rootFilters?.counterparty) ??
(toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
? toNonEmptyString(followupContext?.previous_anchor_value)
: null);
: null) ??
(discoveryEntities[0] ?? null);
const organization = toNonEmptyString(previousFilters?.organization) ??
toNonEmptyString(rootFilters?.organization) ??
(toNonEmptyString(followupContext?.previous_anchor_type) === "organization"
@@ -264,7 +285,7 @@ function hasLifecycleSignal(text) {
return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(text);
}
function hasValueFlowSignal(text) {
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(text);
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|входящ|получ(?:ил|ено|ен)|поступил|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(text);
}
function hasPayoutSignal(text) {
return /(?:\bмы\s+(?:за)?плат|(?:за)?платил|оплатил|перечисл|списан|расход|поставщик|исходящ|supplier|payout|outflow|paid\s+to|payment\s+to)/iu.test(text);
@@ -291,6 +312,12 @@ function hasDocumentEvidenceFollowupSignal(text) {
function hasMovementEvidenceFollowupSignal(text) {
return /(?:\u043f\u043e\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0438\u0449\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a(?:\u0438\u0435|\u0438\u0439)\s+\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|(?:\u043f\u043e\u043a\u0430\u0436\u0438|\u043a\u0430\u043a\u0438\u0435|\u0441\u043f\u0438\u0441\u043e\u043a|\u0434\u0430\u0439|\u0438\u0449\u0438)\s+(?:\u043f\u043b\u0430\u0442[еe]\u0436(?:\u0438|\u0438)?|\u043e\u043f\u0435\u0440\u0430\u0446(?:\u0438\u0438|\u0438\u044e)|\u043f\u0440\u043e\u0432\u043e\u0434\u043a(?:\u0438|\u0430)|\u0441\u043f\u0438\u0441\u0430\u043d(?:\u0438\u044f|\u0438\u0435)|\u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d(?:\u0438\u044f|\u0438\u0435)|payment(?:s)?|transaction(?:s)?|operation(?:s)?|posting(?:s)?|bank\s+operation(?:s)?)|movement(?:s)?\s+(?:then|next)?|(?:then|next)\s+movements?|go\s+to\s+movements?)/iu.test(text);
}
function hasPronounDocumentEvidenceFollowupSignal(text) {
return /(?:\u043f\u043e\s+(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439)\s+(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u0430\u043c|\u044b)?|\u0441\u0447(?:[Рµe]С‚|\u0435\u0442)[-\u2011 ]?\u0444\u0430\u043a\u0442\u0443\u0440(?:\u044b|\u0430)?|\u043d\u0430\u043a\u043b\u0430\u0434\u043d(?:\u044b\u0435|\u0430\u044f)?|\u0430\u043a\u0442(?:\u044b)?))/iu.test(text);
}
function hasPronounMovementEvidenceFollowupSignal(text) {
return /(?:\u043f\u043e\s+(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439)\s+(?:\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u043f\u043b\u0430\u0442[Рµe]\u0436(?:\u0430\u043c|\u0438)?|\u043e\u043f\u0435\u0440\u0430\u0446(?:\u0438\u044f\u043c|\u0438\u0438|\u0438\u044e)|\u043f\u0440\u043e\u0432\u043e\u0434\u043a(?:\u0430\u043c|\u0438)|\u0441\u043f\u0438\u0441\u0430\u043d(?:\u0438\u044f\u043c|\u0438\u0435)|\u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u0435)))/iu.test(text);
}
function hasMetadataDownstreamContinuationSignal(text) {
return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(text);
}
@@ -395,6 +422,9 @@ function semanticNeedFor(input) {
return null;
}
function shouldRunDiscovery(input) {
if (input.forceDiscoveryOverExplicitIntent && input.semanticDataNeed) {
return true;
}
if (input.lifecycleSignal || input.unsupported) {
return true;
}
@@ -439,8 +469,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
const entityResolutionSignal = rawEntityResolutionSignal || Boolean(rawEntityCandidate);
const metadataDocumentHintSignal = hasDocumentEvidenceFollowupSignal(rawText);
const metadataMovementHintSignal = hasMovementEvidenceFollowupSignal(rawText);
const metadataDocumentHintSignal = hasDocumentEvidenceFollowupSignal(rawText) || hasPronounDocumentEvidenceFollowupSignal(rawText);
const metadataMovementHintSignal = hasMovementEvidenceFollowupSignal(rawText) || hasPronounMovementEvidenceFollowupSignal(rawText);
const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family);
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
@@ -485,6 +515,37 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
followupSeed.counterparty &&
!rawLifecycleSignal &&
metadataMovementHintSignal);
const entityResolutionGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
metadataDocumentHintSignal);
const entityResolutionGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
metadataMovementHintSignal);
const groundedValueFlowFollowupApplicable = Boolean(rawValueFlowSignal &&
!rawLifecycleSignal &&
!rawMetadataSignal &&
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1"));
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
metadataMovementHintSignal);
const movementEvidenceGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal &&
!rawValueFlowSignal &&
!rawMetadataSignal &&
metadataDocumentHintSignal);
const metadataGroundedLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" &&
(followupSeed.metadataRouteFamily === "document_evidence" ||
followupSeed.metadataRouteFamily === "movement_evidence") &&
@@ -529,10 +590,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
hasMetadataDownstreamContinuationSignal(rawText));
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable ||
entityResolutionGroundedDocumentFollowupApplicable ||
movementEvidenceGroundedDocumentFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
metadataAmbiguityCollapsedDocumentLaneContinuationApplicable;
const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable ||
metadataAmbiguityResolvedMovementFollowupApplicable ||
entityResolutionGroundedMovementFollowupApplicable ||
documentEvidenceGroundedMovementFollowupApplicable ||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
metadataAmbiguityCollapsedMovementLaneContinuationApplicable;
const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable &&
@@ -586,7 +651,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
entityResolutionSignal
});
const entityCandidates = entityResolutionSignal ? [] : collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
const groundedFollowupEntity = followupSeed.counterparty ?? followupSeed.discoveryEntity;
const entityCandidates = entityResolutionSignal ? [] : [];
if (entityResolutionSignal) {
pushNormalizedEntityResolutionCandidate(entityCandidates, rawEntityCandidate);
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
@@ -596,11 +662,20 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
pushNormalizedEntityResolutionCandidate(entityCandidates, followupSeed.counterparty);
}
else {
pushUnique(entityCandidates, predecomposeEntities.counterparty);
pushUnique(entityCandidates, followupSeed.counterparty);
pushUnique(entityCandidates, rawEntityCandidate);
if (groundedFollowupEntity) {
pushUnique(entityCandidates, groundedFollowupEntity);
}
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
pushScopedEntityCandidate(entityCandidates, candidate, groundedFollowupEntity);
}
pushScopedEntityCandidate(entityCandidates, predecomposeEntities.counterparty, groundedFollowupEntity);
if (!groundedFollowupEntity) {
pushScopedEntityCandidate(entityCandidates, followupSeed.counterparty, null);
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
}
pushScopedEntityCandidate(entityCandidates, rawEntityCandidate, groundedFollowupEntity);
}
if ((rawMetadataSignal || metadataFollowupSeedApplicable) && !followupSeed.counterparty) {
if ((rawMetadataSignal || metadataFollowupSeedApplicable) && !groundedFollowupEntity) {
pushUnique(entityCandidates, followupSeed.discoveryEntity);
pushUnique(entityCandidates, rawMetadataScopeHint);
}
@@ -724,7 +799,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
effectiveMetadataFollowupSeedApplicable ||
metadataAmbiguityLaneClarificationApplicable ||
metadataGroundedMovementLaneApplicable ||
metadataGroundedDocumentLaneApplicable
metadataGroundedDocumentLaneApplicable ||
groundedValueFlowFollowupApplicable,
forceDiscoveryOverExplicitIntent: metadataAmbiguityLaneClarificationApplicable ||
metadataGroundedMovementLaneApplicable ||
metadataGroundedDocumentLaneApplicable ||
groundedValueFlowFollowupApplicable
});
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
const sourceSignal = assistantTurnMeaning
@@ -791,6 +871,21 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if (metadataAmbiguityResolvedMovementFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
}
if (entityResolutionGroundedDocumentFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_document_followup");
}
if (entityResolutionGroundedMovementFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_movement_followup");
}
if (groundedValueFlowFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_grounded_value_flow_followup");
}
if (documentEvidenceGroundedMovementFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_document_evidence_grounded_movement_followup");
}
if (movementEvidenceGroundedDocumentFollowupApplicable) {
pushReason(reasonCodes, "mcp_discovery_movement_evidence_grounded_document_followup");
}
if (metadataGroundedLaneContinuationApplicable) {
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
}
@@ -467,6 +467,7 @@ function createAssistantTransitionPolicy(deps) {
const sourceDiscoveryMetadataSelectedEntitySet = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataSelectedEntitySet)(carryoverSourceDebug, deps.toNonEmptyString);
const sourceDiscoveryMetadataAmbiguityDetected = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityDetected)(carryoverSourceDebug);
const sourceDiscoveryMetadataAmbiguityEntitySets = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityEntitySets)(carryoverSourceDebug, deps.toNonEmptyString);
const sourceDiscoveryEntityCandidates = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryEntityCandidates)(carryoverSourceDebug, deps.toNonEmptyString);
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const llmSelectedObjectScopeDetected = llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true;
const resolvedPrimaryIntent = deps.resolveAddressIntent(deps.repairAddressMojibake(String(userMessage ?? ""))).intent;
@@ -697,6 +698,7 @@ function createAssistantTransitionPolicy(deps) {
previous_anchor_type: previousAnchorType ?? undefined,
previous_anchor_value: previousAnchor,
previous_discovery_pilot_scope: sourceDiscoveryPilotScope ?? undefined,
previous_discovery_entity_candidates: sourceDiscoveryEntityCandidates.length > 0 ? sourceDiscoveryEntityCandidates : undefined,
previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined,
previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined,
previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined,