Inventory breadth: закрепить stock provenance и sale-trace контуры

This commit is contained in:
2026-05-01 11:37:40 +03:00
parent 87c440d6fb
commit b12f370784
34 changed files with 1354 additions and 97 deletions
@@ -109,6 +109,10 @@ function isConfirmedBalanceIntent(intent) {
intent === "vat_liability_confirmed_for_tax_period");
}
function resolveAddressAsOfDateBasis(filters, semanticFrame) {
if (semanticFrame?.date_scope_kind === "implicit_current" &&
semanticFrame.date_basis_hint === "implicit_current_snapshot") {
return "implicit_current_snapshot";
}
const asOfDate = normalizeIsoDateHint(filters.as_of_date);
if (asOfDate) {
return "explicit_as_of_date";
@@ -166,8 +166,11 @@ function toIsoDate(year, month, day) {
}
return `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}
function hasImplicitCurrentAsOfDateCue(text) {
return /\b(сегодня|на\s+сегодня|на\s+текущ(?:ую|ая|ий|ее|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+сегодняшн(?:юю|ий|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+текущ(?:ий|ую)\s+момент|today|as\s+of\s+today|current\s+date|as\s+of\s+current\s+date)\b/i.test(text);
}
function extractAsOfDate(text) {
if (/\b(сегодня|на\s+сегодня|на\s+текущ(?:ую|ая|ий|ее|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+сегодняшн(?:юю|ий|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+текущ(?:ий|ую)\s+момент|today|as\s+of\s+today|current\s+date|as\s+of\s+current\s+date)\b/i.test(text)) {
if (hasImplicitCurrentAsOfDateCue(text)) {
return new Date().toISOString().slice(0, 10);
}
const ymd = text.match(DATE_YMD_PATTERN);
@@ -657,6 +660,8 @@ function isLowQualityCounterpartyAnchorValue(rawValue) {
"ноябрь",
"декабрь"
]);
const isLowQualityTimeToken = (token) => lowQualityTimeTokens.has(token) ||
/^(?:январ|феврал|март|апрел|ма(?:й|я|е)|июн|июл|август|сентябр|октябр|ноябр|декабр)/iu.test(token);
const lowQualityGenericTokens = new Set([
"деньги",
"денег",
@@ -680,13 +685,13 @@ function isLowQualityCounterpartyAnchorValue(rawValue) {
"целом"
]);
const meaningfulNonTemporalTokens = tokens.filter((token) => isLikelyCounterpartyToken(token) &&
!lowQualityTimeTokens.has(token) &&
!isLowQualityTimeToken(token) &&
!/^(?:19|20)\d{2}$/.test(token));
if (meaningfulNonTemporalTokens.length === 0 && hasTemporalCue) {
return true;
}
const meaningfulNonGenericTokens = tokens.filter((token) => isLikelyCounterpartyToken(token) &&
!lowQualityTimeTokens.has(token) &&
!isLowQualityTimeToken(token) &&
!lowQualityGenericTokens.has(token) &&
!/^(?:19|20)\d{2}$/.test(token));
if (meaningfulNonGenericTokens.length === 0 && (hasTemporalCue || paymentCue)) {
@@ -1133,6 +1138,9 @@ function isTemporalWarehousePhrase(candidate) {
.toLowerCase()
.replace(/ё/g, "е")
.trim();
if (/^(?:в|на)?\s*(?:сейчас|сегодня|текущ(?:ий|ую|ем|его)\s+момент|данн(?:ый|ую|ом|ого)\s+момент)$/iu.test(normalized)) {
return true;
}
if (/^(?:на\s+)?(?:эту|ту|текущ(?:ую|ая|ий|ее|ей)|сегодняшн(?:юю|ая|ий|ее|ей))(?:\s+же)?\s+дат(?:у|а|е|ой)$/iu.test(normalized)) {
return true;
}
@@ -1177,6 +1185,12 @@ function isLowQualityWarehouseAnchorValue(rawValue) {
"лежали",
"на",
"по",
"компания",
"компании",
"компанию",
"организация",
"организации",
"организацию",
"складе",
"складу",
"складом",
@@ -1195,7 +1209,10 @@ function isLowQualityWarehouseAnchorValue(rawValue) {
if (tokens.length === 0) {
return true;
}
const meaningfulTokens = tokens.filter((token) => !lowQualityTokens.has(token) && token.length > 1);
const isLowQualityWarehouseToken = (token) => lowQualityTokens.has(token) ||
/^(?:19|20)\d{2}$/.test(token) ||
/^(?:январ|феврал|март|апрел|ма(?:й|я|е)|июн|июл|август|сентябр|октябр|ноябр|декабр)/iu.test(token);
const meaningfulTokens = tokens.filter((token) => !isLowQualityWarehouseToken(token) && token.length > 1);
if (meaningfulTokens.length === 0) {
return true;
}
@@ -1256,11 +1273,13 @@ function extractInventoryWarehouseAnchor(text) {
return undefined;
}
function extractInventorySupplierAnchor(text) {
const match = String(text ?? "").match(/(?:от\s+поставщика|у\s+поставщика|поставщика|поставщику)\s+([^\r\n?]+?)(?=$|[?])/iu);
const match = String(text ?? "").match(/(?:от\s+поставщика|у\s+поставщика|поставщик(?:а|у|ом)?|supplier|vendor)\s+([^\r\n?]+?)(?=$|[?])/iu);
if (!match?.[1]) {
return undefined;
}
const candidate = cleanupAnchorValue(cleanupAnchorValue(String(match[1])).replace(/\s+(?:сейчас|на\s+склад(?:е|у|ом)?|на\s+дату|по\s+состоянию\s+на\s+дату|котор(?:ый|ые|ых)|куплен(?:ы|а|о)?|были|был|лежат|лежит|еще|ещё|организац\w*|компани\w*).*$/iu, ""));
const candidate = cleanupAnchorValue(cleanupAnchorValue(String(match[1]))
.replace(/\s+(?:сейчас|на\s+склад(?:е|у|ом)?|на\s+дату|по\s+состоянию\s+на\s+дату|котор(?:ый|ые|ых)|куплен(?:ы|а|о)?|были|был|лежат|лежит|еще|ещё|организац\w*|компани\w*).*$/iu, "")
.replace(/\s*(?:->|=>|→)\s*(?:товар|позици|номенклатур|item|product|sku)[\s\S]*$/iu, ""));
if (!candidate ||
isLowQualityCounterpartyAnchorValue(candidate) ||
/^(?:были|был|куплен|куплены|которые|который|которых|сейчас|лежат|лежит)\b/iu.test(candidate)) {
@@ -1369,7 +1388,7 @@ function resolveSemanticDateScopeKind(filters, warnings) {
return "none";
}
function resolveSemanticDateBasisHint(filters, warnings) {
if (warnings.includes("as_of_date_defaulted_today")) {
if (warnings.includes("as_of_date_defaulted_today") || warnings.includes("as_of_date_from_implicit_current_phrase")) {
return "implicit_current_snapshot";
}
const hasAsOfDate = typeof filters.as_of_date === "string" && filters.as_of_date.trim().length > 0;
@@ -1470,6 +1489,7 @@ function extractAddressFilters(userMessage, intent) {
}
}
const warnings = [];
const implicitCurrentAsOfDateCue = hasImplicitCurrentAsOfDateCue(text);
const explicitAsOfDate = extractAsOfDate(text);
const explicitAsOfDateWithCue = extractAsOfDateWithCue(text);
const accountMatch = text.match(ACCOUNT_REVERSE_PATTERN) ?? text.match(ACCOUNT_PATTERN);
@@ -1510,6 +1530,13 @@ function extractAddressFilters(userMessage, intent) {
filters.counterparty = supplierAnchor;
}
}
if (intent === "inventory_purchase_to_sale_chain") {
const supplierAnchor = asksForInventorySupplierIdentity(text) ? undefined : extractInventorySupplierAnchor(text);
if (supplierAnchor) {
filters.counterparty = supplierAnchor;
warnings.push("supplier_anchor_derived_for_inventory_documentary_chain");
}
}
const allowGenericCounterpartyAnchor = !isInventoryTraceIntent(intent);
const counterpartyMatch = allowGenericCounterpartyAnchor ? text.match(COUNTERPARTY_PATTERN) : null;
if (counterpartyMatch && !filters.counterparty) {
@@ -1655,6 +1682,9 @@ function extractAddressFilters(userMessage, intent) {
}
if (usesAsOfPrimaryWindow(intent) && explicitAsOfDate) {
filters.as_of_date = explicitAsOfDate;
if (implicitCurrentAsOfDateCue && !warnings.includes("as_of_date_from_implicit_current_phrase")) {
warnings.push("as_of_date_from_implicit_current_phrase");
}
const periodWasDerivedHeuristically = warnings.includes("period_derived_from_month_phrase") ||
warnings.includes("period_derived_from_year_range_phrase") ||
warnings.includes("period_derived_from_year_phrase");
@@ -1670,6 +1700,9 @@ function extractAddressFilters(userMessage, intent) {
const asOfDate = extractAsOfDate(text);
if (asOfDate) {
filters.as_of_date = asOfDate;
if (implicitCurrentAsOfDateCue && !warnings.includes("as_of_date_from_implicit_current_phrase")) {
warnings.push("as_of_date_from_implicit_current_phrase");
}
}
}
// For counterparty document/bank lists we keep period open by default (all-time over available data)
@@ -1416,6 +1416,12 @@ function hasInventoryPurchaseDocumentsSignalV2(text) {
return hasItemCue && hasPurchaseDocCue;
}
function hasInventorySaleTraceSignalV2(text) {
const value = String(text ?? "");
const hasPlainItemCue = /(?:\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|\u043f\u043e\u0437\u0438\u0446|\u043f\u0440\u043e\u0434\u0443\u043a\u0446\u0438|sku|item|product)/iu.test(value);
const hasPlainTraceCue = /(?:\u043a\u043e\u043c\u0443\s+(?:\u0432\s+\u0438\u0442\u043e\u0433\u0435\s+)?(?:\u043c\u044b\s+)?(?:\u043f\u0440\u043e\u0434\u0430\u043b\u0438|\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438|\u0432\u043f\u0430\u0440\u0438\u043b\u0438)|\u043a\u043e\u043c\u0443\s+(?:\u0431\u044b\u043b[\u0430\u0438\u043e]?|\u0431\u044b\u043b\u0438)?\s*(?:\u043f\u0440\u043e\u0434\u0430\u043d|\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d)|\u043a\u0442\u043e\s+\u043a\u0443\u043f\u0438\u043b|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b|buyer|sale\s+trace|trace\s+of\s+sale)/iu.test(value);
if (hasPlainItemCue && (hasPlainTraceCue || (0, inventoryLifecycleCueHelpers_1.hasInventorySaleCue)(value))) {
return true;
}
const hasItemCue = /(?:товар|номенклатур|sku|item|product|позици(?:я|ю|и)|продукци(?:я|ю|и))/iu.test(text);
const hasTraceCue = /(?:кому\s+(?:в\s+итоге\s+)?(?:мы\s+)?продали|кому\s+был\s+продан|куда\s+(?:в\s+итоге\s+)?(?:мы\s+)?продали(?:\s+(?:это|его|товар|позицию))?|куда\s+(?:была\s+)?реализована\s+(?:позиция|номенклатура|продукция)|кто\s+купил|buyer|sale\s+trace|trace\s+of\s+sale|через\s+какие\s+документы\s+прош[её]л\s+путь\s+товара|закупк.*склад.*продаж|purchase[\s-]?to[\s-]?sale|purchase\s*->\s*warehouse\s*->\s*sale|purchase\s*->\s*stock\s*->\s*sale)/iu.test(text);
return hasItemCue && hasTraceCue;
@@ -1435,6 +1441,12 @@ function hasInventoryAgingSignal(text) {
return hasAgingCue || (hasResidueCue && /(?:давно\s+куплен|давно\s+приобретен|задолго\s+до)/iu.test(text));
}
function hasInventoryPurchaseToSaleChainSignal(text) {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|позици|sku|item|product)/iu.test(value);
const hasPlainChainCue = /(?:закупк[а-яё]*\s*->\s*склад\s*->\s*продаж|закупк[а-яё]*[\s\S]{0,80}склад[\s\S]{0,80}продаж|через\s+какие\s+документы\s+прош[её]л\s+путь|путь\s+товар[а-яё]*[\s\S]{0,80}закуп|цепочк[а-яё]*\s+движен|документально\s+подтвержденн[а-яё]*\s+цепочк|supplier\s*->\s*item\s*->\s*(?:buyer|customer)|supplier\s+to\s+buyer|supplier\s+to\s+item\s+to\s+buyer|purchase[\s-]?to[\s-]?sale|purchase\s*->\s*(?:warehouse|stock)\s*->\s*sale)/iu.test(value) || value.includes("->");
if (hasPlainItemCue && hasPlainChainCue) {
return true;
}
const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text);
const hasChainCue = /(?:закупк.*склад.*продаж|purchase[\s-]?to[\s-]?sale|purchase\s*->\s*(?:warehouse|stock)\s*->\s*sale|закупка\s*->\s*склад\s*->\s*продажа|цепочк[аи]\s+движен|документально\s+подтвержденн\w+\s+цепочк|supplier\s*->\s*item\s*->\s*(?:buyer|customer)|supplier\s+to\s+buyer|supplier\s+to\s+item\s+to\s+buyer)/iu.test(text) || text.includes("->");
return hasItemCue && hasChainCue;
@@ -1603,6 +1615,10 @@ function resolveUnicodeAddressIntentBridge(text) {
]).has(byAnchorToken);
const hasMoneyCue = /(?:деньг|денег|выручк|доход|оборот|заработ|прин[её]с|чек|ликвидн|revenue|turnover|money)/iu.test(normalized);
const hasRankingCue = /(?:топ|ранк|сам(?:ый|ая|ое|ые)|больше\s+всего|наибольш|крупн|жирн|max|top|rank)/iu.test(normalized);
const hasInventoryPurchaseToSaleDocumentChainCue = /(?:закупк[а-яё]*[\s\S]{0,80}склад[\s\S]{0,80}продаж|путь\s+товар[а-яё]*[\s\S]{0,80}закуп|purchase\s*->\s*(?:warehouse|stock)\s*->\s*sale|->\s*(?:склад|warehouse|stock)\s*->\s*(?:продаж|sale))/iu.test(normalized) && /(?:товар|позици|номенклатур|sku|item|product)/iu.test(normalized);
if (hasInventoryPurchaseToSaleDocumentChainCue) {
return unicodeBridgeResolution("inventory_purchase_to_sale_chain", "high", "unicode_inventory_purchase_to_sale_chain_bridge_signal_detected");
}
const hasOpenItemsAccountCue = /(?:хвост|долг|незакрыт|вис)/iu.test(normalized) &&
/(?:сч(?:е|ё)т(?:а|у|ом|е|ов)?\s*(?:№|#)?\s*(?:60|62|76)(?:[.,]\d{1,2})?|\b(?:60|62|76)(?:[.,]\d{1,2})?\b\s*сч(?:е|ё)т)/iu.test(normalized);
if (hasOpenItemsAccountCue) {
@@ -89,9 +89,13 @@ function hasInventoryProvenanceSignalV2(text) {
return hasItemCue && hasSupplierCue && hasPurchaseCue;
}
function hasInventoryPurchaseDateSignal(text) {
const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text) || hasSelectedObjectInventoryCue(text);
const hasPurchaseDateCue = /(?:когда\s+(?:примерно\s+)?(?:мы\s+)?купили|когда\s+был\s+куплен|когда\s+куплен|дата\s+закупк|purchase\s+date)/iu.test(text) ||
/(?:когда\s+был(?:а|и|о)?\s+закупк\w*|когда\s+закупк\w*)/iu.test(text);
const value = String(text ?? "");
const hasItemCue = /(?:\u0442\u043e\u0432\u0430\u0440|\u043f\u043e\u0437\u0438\u0446|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|sku|item|product)/iu.test(value) ||
/(?:товар|номенклатур|sku|item|product)/iu.test(value) ||
hasSelectedObjectInventoryCue(value);
const hasPurchaseDateCue = /(?:\u043a\u043e\u0433\u0434\u0430\s+(?:\u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e\s+)?(?:\u043c\u044b\s+)?\u043a\u0443\u043f\u0438\u043b\u0438|\u043a\u043e\u0433\u0434\u0430\s+\u0431\u044b\u043b(?:\u0430|\u0438|\u043e)?\s+\u043a\u0443\u043f\u043b\u0435\u043d|\u043a\u043e\u0433\u0434\u0430\s+\u043a\u0443\u043f\u043b\u0435\u043d|\u0434\u0430\u0442\u0430\s+\u0437\u0430\u043a\u0443\u043f\u043a|purchase\s+date)/iu.test(value) ||
/(?:когда\s+(?:примерно\s+)?(?:мы\s+)?купили|когда\s+был\s+куплен|когда\s+куплен|дата\s+закупк|purchase\s+date)/iu.test(value) ||
/(?:когда\s+был(?:а|и|о)?\s+закупк\w*|когда\s+закупк\w*)/iu.test(value);
return hasItemCue && hasPurchaseDateCue;
}
function hasInventoryPurchaseDocumentsSignalV2(text) {
@@ -108,7 +112,7 @@ function hasInventoryPurchaseDocumentsSignalV2(text) {
function hasInventorySaleTraceSignalV2(text) {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|позици|продукци|sku|item|product)/iu.test(value);
const hasPlainTraceCue = /(?:кому\s+(?:в\s+итоге\s+)?(?:мы\s+)?(?:продали|реализовали|впарили)|кому\s+(?:был[аио]?|были)?\s*реализован|кто\s+купил|покупател|buyer|sale\s+trace|trace\s+of\s+sale)/iu.test(value);
const hasPlainTraceCue = /(?:кому\s+(?:в\s+итоге\s+)?(?:мы\s+)?(?:продали|реализовали|впарили)|кому\s+(?:был[аио]?|были)?\s*(?:продан|реализован)|кто\s+купил|покупател|buyer|sale\s+trace|trace\s+of\s+sale)/iu.test(value);
if (hasPlainItemCue && hasPlainTraceCue) {
return true;
}
+128 -4
View File
@@ -211,6 +211,20 @@ function deriveTaxQuarterWindowForDate(value) {
period_to: `${year}-${String(quarterEndMonth).padStart(2, "0")}-${String(quarterEndDay).padStart(2, "0")}`
};
}
function deriveMonthWindowForDate(value) {
const isoDate = normalizeIsoDateForQuery(value);
if (!isoDate) {
return null;
}
const match = isoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
return {
period_from: `${match[1]}-${match[2]}-01`,
period_to: isoDate
};
}
function toDateTimeExprForQuery(isoDate) {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
@@ -1173,6 +1187,8 @@ function toNormalizedRows(rows) {
const item = resolveInventoryItemFromRawRow(row, accountDt, accountKt);
const warehouse = firstNonEmptyString(row.Склад, row.Warehouse, row.warehouse, row.СкладПредставление);
const organization = firstNonEmptyString(row.Организация, row.Organization, row.organization, row.organization_name, row.ОрганизацияПредставление);
const counterparty = firstNonEmptyString(row.Контрагент, row.Counterparty, row.counterparty);
const contract = firstNonEmptyString(row.Договор, row.Contract, row.contract);
const analytics = collectAnalyticsStrings(row);
return {
period,
@@ -1184,7 +1200,9 @@ function toNormalizedRows(rows) {
quantity,
item,
warehouse,
organization
organization,
counterparty,
contract
};
})
.filter((item) => Boolean(item.period || item.registrator));
@@ -1235,6 +1253,10 @@ function formatMoneyRubForReply(value) {
}).format(value)} ₽`;
}
function extractContractNameFromNormalizedRow(row) {
const explicitContract = firstNonEmptyString(row.contract);
if (explicitContract) {
return explicitContract;
}
for (const token of row.analytics) {
const normalized = String(token ?? "").trim();
if (!normalized) {
@@ -1539,6 +1561,10 @@ function applyPreExecutionOrganizationScopeGrounding(input) {
]);
const resolvedOrganizationFromMessage = (0, assistantOrganizationMatcher_1.resolveOrganizationSelectionFromMessage)(input.userMessage, candidateOrganizations);
const referentialOrganizationScopeDetected = hasReferentialOrganizationScopeSignal(input.userMessage);
const counterpartyAnchorProtectsOrganizationScope = input.semanticFrame?.anchor_kind === "counterparty" &&
typeof input.filters.counterparty === "string" &&
input.filters.counterparty.trim().length > 0 &&
!referentialOrganizationScopeDetected;
if (!input.filters.organization &&
input.semanticFrame?.scope_kind === "implicit_self_scope" &&
activeOrganization) {
@@ -1552,6 +1578,7 @@ function applyPreExecutionOrganizationScopeGrounding(input) {
}
if (resolvedOrganizationFromMessage &&
(!input.filters.organization || input.semanticFrame?.anchor_kind === "organization") &&
!counterpartyAnchorProtectsOrganizationScope &&
!sameNormalizedOrganizationScope(input.filters.organization ?? null, resolvedOrganizationFromMessage)) {
input.filters.organization = resolvedOrganizationFromMessage;
if (!input.warnings.includes("organization_grounded_from_scope_candidates")) {
@@ -1921,6 +1948,9 @@ function hasExplicitPeriodWindow(filters) {
return ((typeof filters.period_from === "string" && filters.period_from.trim().length > 0) ||
(typeof filters.period_to === "string" && filters.period_to.trim().length > 0));
}
function asksForUnresolvedInventorySupplierLink(userMessage) {
return /(?:\u0431\u0435\u0437\s+\u043f\u043e\u043d\u044f\u0442\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0431\u0435\u0437\s+(?:\u044f\u0432\u043d[^\s]*\s+)?\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043d\u0435\s+\u0438\u043c\u0435\u044e\u0442\s+\u044f\u0432\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043d\u0435\u0442\s+\u044f\u0432\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|unresolved\s+supplier\s+link)/iu.test(String(userMessage ?? ""));
}
function canAutoBroadenPeriodWindow(intent, filters) {
const hasRecoverableAsOfOnlyWindow = !hasExplicitPeriodWindow(filters) &&
typeof filters.as_of_date === "string" &&
@@ -1956,11 +1986,14 @@ function shouldClearAsOfDateForHistoryRecovery(intent) {
intent === "inventory_purchase_to_sale_chain");
}
function shouldDetachLifecycleExecutionFromSnapshotContext(intent, reasons) {
if (intent !== "inventory_sale_trace_for_item" &&
if (intent !== "inventory_supplier_stock_overlap_as_of_date" &&
intent !== "inventory_sale_trace_for_item" &&
intent !== "inventory_purchase_to_sale_chain") {
return false;
}
return (reasons.includes("as_of_date_from_followup_context") ||
return (reasons.includes("period_window_semantic_from_inventory_snapshot_context") ||
reasons.includes("period_window_semantic_from_inventory_as_of_month") ||
reasons.includes("as_of_date_from_followup_context") ||
reasons.includes("period_from_followup_context") ||
reasons.includes("as_of_date_from_open_items_followup_context"));
}
@@ -2820,6 +2853,84 @@ class AddressQueryService {
});
const knownOrganizations = (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(options.knownOrganizations ?? []);
const activeOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(options.activeOrganization ?? null);
const previousOrganizationFromContext = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(followupContext?.previous_filters?.organization ?? null);
const chainCounterpartyAnchor = toNonEmptyFilterValue(filters.extracted_filters.counterparty);
const chainOrganizationAnchor = toNonEmptyFilterValue(filters.extracted_filters.organization);
if (intent.intent === "inventory_purchase_to_sale_chain" &&
chainCounterpartyAnchor &&
chainOrganizationAnchor &&
sameOrganizationEntityReference(chainOrganizationAnchor, chainCounterpartyAnchor)) {
delete filters.extracted_filters.organization;
const restoredOrganization = activeOrganization ?? previousOrganizationFromContext;
if (restoredOrganization && !sameOrganizationEntityReference(restoredOrganization, chainCounterpartyAnchor)) {
filters.extracted_filters.organization = restoredOrganization;
if (!filters.warnings.includes("organization_restored_from_inventory_chain_context")) {
filters.warnings.push("organization_restored_from_inventory_chain_context");
}
if (!baseReasons.includes("organization_restored_from_inventory_chain_context")) {
baseReasons.push("organization_restored_from_inventory_chain_context");
}
}
else {
if (!filters.warnings.includes("organization_cleared_from_inventory_chain_counterparty_anchor")) {
filters.warnings.push("organization_cleared_from_inventory_chain_counterparty_anchor");
}
if (!baseReasons.includes("organization_cleared_from_inventory_chain_counterparty_anchor")) {
baseReasons.push("organization_cleared_from_inventory_chain_counterparty_anchor");
}
}
}
if (intent.intent === "inventory_supplier_stock_overlap_as_of_date" &&
(followupContext?.root_filters || followupContext?.previous_filters) &&
!toNonEmptyFilterValue(filters.extracted_filters.period_from) &&
!toNonEmptyFilterValue(filters.extracted_filters.period_to) &&
!/(?:за\s+вс[её]\s+время|за\s+любой\s+период|all[\s-]?time|all\s+periods?)/iu.test(userMessage)) {
const snapshotContextFilters = followupContext?.root_filters &&
(toNonEmptyFilterValue(followupContext.root_filters.period_from) ||
toNonEmptyFilterValue(followupContext.root_filters.period_to))
? followupContext.root_filters
: followupContext?.previous_filters;
const previousPeriodFrom = toNonEmptyFilterValue(snapshotContextFilters?.period_from);
const previousPeriodTo = toNonEmptyFilterValue(snapshotContextFilters?.period_to);
if (previousPeriodFrom || previousPeriodTo) {
filters.extracted_filters = {
...filters.extracted_filters,
...(previousPeriodFrom ? { period_from: previousPeriodFrom } : {}),
...(previousPeriodTo ? { period_to: previousPeriodTo } : {})
};
if (!toNonEmptyFilterValue(filters.extracted_filters.as_of_date)) {
const inheritedAsOfDate = toNonEmptyFilterValue(snapshotContextFilters?.as_of_date) ?? previousPeriodTo ?? previousPeriodFrom;
if (inheritedAsOfDate) {
filters.extracted_filters.as_of_date = inheritedAsOfDate;
}
}
if (!filters.warnings.includes("period_window_semantic_from_inventory_snapshot_context")) {
filters.warnings.push("period_window_semantic_from_inventory_snapshot_context");
}
if (!baseReasons.includes("period_window_semantic_from_inventory_snapshot_context")) {
baseReasons.push("period_window_semantic_from_inventory_snapshot_context");
}
}
}
if (intent.intent === "inventory_supplier_stock_overlap_as_of_date" &&
!toNonEmptyFilterValue(filters.extracted_filters.period_from) &&
!toNonEmptyFilterValue(filters.extracted_filters.period_to) &&
asksForUnresolvedInventorySupplierLink(userMessage) &&
!/(?:за\s+вс[её]\s+время|за\s+любой\s+период|all[\s-]?time|all\s+periods?)/iu.test(userMessage)) {
const monthWindow = deriveMonthWindowForDate(filters.extracted_filters.as_of_date);
if (monthWindow) {
filters.extracted_filters = {
...filters.extracted_filters,
...monthWindow
};
if (!filters.warnings.includes("period_window_semantic_from_inventory_as_of_month")) {
filters.warnings.push("period_window_semantic_from_inventory_as_of_month");
}
if (!baseReasons.includes("period_window_semantic_from_inventory_as_of_month")) {
baseReasons.push("period_window_semantic_from_inventory_as_of_month");
}
}
}
if (isOrganizationScopedValueFlowIntent(intent.intent) &&
hasExplicitSingleOrganizationValueFlowScopeRequest(userMessage) &&
!resolvedOrganizationFromMessage) {
@@ -2969,13 +3080,14 @@ class AddressQueryService {
const detachedExecutionFilters = { ...executionFilters };
let periodDetached = false;
let asOfDetached = false;
const keepAsOfDateForInventorySnapshotOverlap = intent.intent === "inventory_supplier_stock_overlap_as_of_date";
if (toNonEmptyFilterValue(detachedExecutionFilters.period_from) ||
toNonEmptyFilterValue(detachedExecutionFilters.period_to)) {
delete detachedExecutionFilters.period_from;
delete detachedExecutionFilters.period_to;
periodDetached = true;
}
if (toNonEmptyFilterValue(detachedExecutionFilters.as_of_date)) {
if (!keepAsOfDateForInventorySnapshotOverlap && toNonEmptyFilterValue(detachedExecutionFilters.as_of_date)) {
delete detachedExecutionFilters.as_of_date;
asOfDetached = true;
}
@@ -3452,6 +3564,18 @@ class AddressQueryService {
: anchor.anchor_type === "contract" && anchor.anchor_value_resolved
? { ...executionFilters, contract: anchor.anchor_value_resolved }
: executionFilters;
if (intent.intent === "inventory_purchase_to_sale_chain" &&
toNonEmptyFilterValue(filtersForMatching.item) &&
toNonEmptyFilterValue(filtersForMatching.counterparty)) {
filtersForMatching = { ...filtersForMatching };
delete filtersForMatching.counterparty;
if (!filters.warnings.includes("inventory_chain_counterparty_anchor_kept_for_verification")) {
filters.warnings.push("inventory_chain_counterparty_anchor_kept_for_verification");
}
if (!baseReasons.includes("inventory_chain_counterparty_anchor_kept_for_verification")) {
baseReasons.push("inventory_chain_counterparty_anchor_kept_for_verification");
}
}
const accountScopeAudit = buildAccountScopeAudit({
intent: intent.intent,
filters: filtersForMatching,
@@ -1235,6 +1235,26 @@ function buildInventoryPurchaseDocumentQuery(filters, resolvedLimit) {
.replace("__WHERE_CLAUSE__", buildWhereClause(filters, "Товары.Ссылка.Дата", ['Товары.Ссылка.Проведен = ИСТИНА', itemCondition].filter((item) => Boolean(item))))
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
}
function stripTrailingOrderBy(query) {
return String(query ?? "").replace(/\r?\nУПОРЯДОЧИТЬ ПО[\s\S]*$/u, "").trimEnd();
}
function removeTopLimit(query) {
return String(query ?? "").replace(/ВЫБРАТЬ ПЕРВЫЕ\s+\d+/u, "ВЫБРАТЬ");
}
function buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit) {
const purchaseQuery = removeTopLimit(stripTrailingOrderBy(buildInventoryPurchaseDocumentQuery(filters, resolvedLimit)));
const saleQuery = removeTopLimit(stripTrailingOrderBy(buildInventorySaleDocumentQuery(filters, resolvedLimit)));
return [
purchaseQuery,
"",
"ОБЪЕДИНИТЬ ВСЕ",
"",
saleQuery,
"",
"УПОРЯДОЧИТЬ ПО",
` Период ${resolveOrderDirection(filters.sort)}`
].join("\n");
}
function buildCounterpartyPurchaseDocumentQuery(filters, resolvedLimit) {
const goodsCounterpartyCondition = buildCounterpartyReferenceCondition(filters, ["Товары.Ссылка.Контрагент"]);
const servicesCounterpartyCondition = buildCounterpartyReferenceCondition(filters, ["Услуги.Ссылка.Контрагент"]);
@@ -1463,9 +1483,9 @@ function buildAddressRecipePlan(recipe, filters) {
: recipe.query_template === "inventory_supplier_stock_overlap_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_sale_trace_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "kt")
? buildInventorySaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_purchase_to_sale_chain_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "either")
? buildInventoryPurchaseToSaleDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_aging_by_purchase_date_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "contracts_by_counterparty_profile"
@@ -848,6 +848,16 @@ function extractInventoryCounterpartyCandidates(row, excludedTokens = []) {
}
candidates.push(normalized);
}
const explicitCounterparty = normalizeCounterpartyDisplayLabel(row.counterparty);
const explicitComparable = normalizeEntityToken(explicitCounterparty);
if (explicitCounterparty &&
explicitComparable &&
explicitComparable !== itemToken &&
explicitComparable !== warehouseToken &&
explicitComparable !== organizationToken &&
!excludedComparableTokens.includes(explicitComparable)) {
candidates.unshift(explicitCounterparty);
}
return uniqueStrings(candidates);
}
function summarizeInventoryTraceRows(rows, excludedCounterpartyTokens = []) {
@@ -883,6 +893,7 @@ function summarizeInventoryTraceRows(rows, excludedCounterpartyTokens = []) {
function formatInventoryTraceRows(rows, limit = 10, excludedCounterpartyTokens = []) {
return rows.slice(0, limit).map((row, index) => {
const parties = extractInventoryCounterpartyCandidates(row, excludedCounterpartyTokens);
const item = extractInventoryItemName(row);
const warehouse = extractInventoryWarehouseName(row);
const organization = extractInventoryOrganizationName(row);
const amount = typeof row.amount === "number" && Number.isFinite(row.amount) ? formatMoneyRub(row.amount) : "сумма не указана";
@@ -891,6 +902,9 @@ function formatInventoryTraceRows(rows, limit = 10, excludedCounterpartyTokens =
`дата: ${inventoryTraceDateLabel(row.period)}`,
`сумма: ${amount}`
];
if (item) {
parts.push(`товар: ${item}`);
}
if (warehouse) {
parts.push(`склад: ${warehouse}`);
}
@@ -3,6 +3,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.composeInventoryReply = composeInventoryReply;
const replyContracts_1 = require("./replyContracts");
const inventoryReplyPresentation_1 = require("./inventoryReplyPresentation");
function cleanupInventoryRequestedParty(value) {
const cleaned = String(value ?? "")
.replace(/\s*(?:->|=>|→)\s*(?:товар|позици|номенклатур|покупател|buyer|customer|item|product|sku)[\s\S]*$/iu, "")
.replace(/\s+(?:на\s+дату|по\s+состоянию|за\s+период)\b[\s\S]*$/iu, "")
.replace(/[«»"]/gu, "")
.replace(/[.,;:\s]+$/u, "")
.trim();
return cleaned.length > 0 ? cleaned : null;
}
function extractRequestedInventoryParty(userMessage, role) {
const text = String(userMessage ?? "");
const patterns = role === "supplier"
? [
/(?:от\s+поставщика|у\s+поставщика|поставщик(?:а|у|ом)?|supplier|vendor)\s+([^\r\n?]+?)(?=$|[?]|(?:\s*(?:->|=>|→)\s*(?:товар|позици|номенклатур|item|product|sku|покупател|buyer|customer)))/iu
]
: [
/(?:покупател(?:ь|я|ю|ем)?|buyer|customer|client)\s+([^\r\n?]+?)(?=$|[?]|(?:\s*(?:->|=>|→))|(?:\s+на\s+дату))/iu
];
for (const pattern of patterns) {
const match = text.match(pattern);
const candidate = match?.[1] ? cleanupInventoryRequestedParty(match[1]) : null;
if (candidate) {
return candidate;
}
}
return null;
}
function inventoryPartyComparableTokens(value) {
const stopWords = new Set(["ооо", "ао", "пао", "зао", "ип", "llc", "ltd", "inc", "corp"]);
return String(value ?? "")
.toLowerCase()
.replace(/ё/gu, "е")
.replace(/[^a-zа-я0-9]+/giu, " ")
.split(/\s+/u)
.map((token) => token.trim())
.filter((token) => token.length >= 3 && !stopWords.has(token));
}
function inventoryRequestedPartyMatches(requested, actualParties) {
if (!requested) {
return true;
}
const requestedTokens = inventoryPartyComparableTokens(requested);
if (requestedTokens.length === 0) {
return false;
}
return actualParties.some((actual) => {
const actualTokens = inventoryPartyComparableTokens(actual);
return requestedTokens.every((token) => actualTokens.includes(token));
});
}
function inventoryPartyListOrUnknown(parties) {
return parties.length > 0 ? parties.slice(0, 4).join("; ") : "не выделен отдельным полем";
}
function composeInventoryReply(intent, rows, options, deps) {
if (intent === "inventory_on_hand_as_of_date") {
const asOfDate = deps.resolvePayablesAsOfDate(options);
@@ -163,6 +216,29 @@ function composeInventoryReply(intent, rows, options, deps) {
const purchaseRows = rows.filter((row) => deps.isInventoryPurchaseMovement(row));
const summary = deps.summarizeInventoryTraceRows(purchaseRows);
const unresolvedRows = purchaseRows.filter((row) => deps.extractInventoryCounterpartyCandidates(row).length === 0);
const unresolvedSupplierQuestion = /(?:\u0431\u0435\u0437\s+\u043f\u043e\u043d\u044f\u0442\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u0431\u0435\u0437\s+(?:\u044f\u0432\u043d[^\s]*\s+)?\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043d\u0435\s+\u0438\u043c\u0435\u044e\u0442\s+\u044f\u0432\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043d\u0435\u0442\s+\u044f\u0432\u043d[^\s]*\s+\u043f\u0440\u0438\u0432\u044f\u0437\u043a[^\s]*\s+\u043a\s+\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|unresolved\s+supplier\s+link)/iu.test(String(options.userMessage ?? ""));
if (unresolvedSupplierQuestion) {
const directAnswerLine = unresolvedRows.length > 0
? `В текущем складском срезе найдено операций без явно выделенного поставщика: ${deps.formatNumberWithDots(unresolvedRows.length)}.`
: "В текущем складском срезе товары без явно выделенной привязки к поставщику в доступных данных не найдены.";
const lines = [directAnswerLine];
(0, inventoryReplyPresentation_1.appendInventoryBulletSection)(lines, "Что проверили:", [
`Дата среза: ${deps.formatDateRu(asOfDate)}.`,
`Закупочных операций в выборке: ${deps.formatNumberWithDots(purchaseRows.length)}.`,
`Операций без явно выделенного поставщика: ${deps.formatNumberWithDots(unresolvedRows.length)}.`,
`Поставщиков, выделенных в остальных операциях: ${deps.formatNumberWithDots(summary.counterparties.length)}.`
]);
(0, inventoryReplyPresentation_1.appendInventoryBulletSection)(lines, "Ограничения:", [
"Без партионного учета это проверка доступного закупочного следа по складскому срезу, а не юридическое доказательство владельца каждой партии."
]);
if (unresolvedRows.length > 0) {
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Позиции без явно выделенного поставщика:", deps.formatInventoryTraceRows(unresolvedRows, 12));
}
else if (summary.counterparties.length > 0) {
lines.push(`- В доступном закупочном следе встречаются поставщики: ${summary.counterparties.slice(0, 6).join("; ")}.`);
}
return (0, replyContracts_1.buildFactualSummaryReply)(lines, (0, replyContracts_1.buildConfirmedBalanceSemantics)(unresolvedRows.length > 0 ? "medium" : "strong", true));
}
const warehouseLabel = summary.warehouses[0] ?? "не указанного склада";
const directAnswerLine = summary.counterparties.length === 1
? `По складскому остатку ${warehouseLabel} выявлен поставщик: ${summary.counterparties[0]}.`
@@ -283,12 +359,25 @@ function composeInventoryReply(intent, rows, options, deps) {
const purchaseSummary = deps.summarizeInventoryTraceRows(purchaseRows);
const saleSummary = deps.summarizeInventoryTraceRows(saleRows);
const itemLabel = purchaseSummary.item ?? saleSummary.item ?? "товар не определен";
const directAnswerLine = purchaseSummary.counterparties.length === 1 && saleSummary.counterparties.length === 1
? `По товару ${itemLabel} цепочка поставки и продажи связана с поставщиком ${purchaseSummary.counterparties[0]} и покупателем ${saleSummary.counterparties[0]}.`
: `По товару ${itemLabel} цепочка поставки и продажи подтверждена частично или разнообразно: детали идут следом.`;
const requestedSupplier = extractRequestedInventoryParty(options.userMessage, "supplier");
const requestedBuyer = extractRequestedInventoryParty(options.userMessage, "buyer");
const supplierMatches = inventoryRequestedPartyMatches(requestedSupplier, purchaseSummary.counterparties);
const buyerMatches = inventoryRequestedPartyMatches(requestedBuyer, saleSummary.counterparties);
const mismatchParts = [];
if (requestedSupplier && purchaseRows.length > 0 && !supplierMatches) {
mismatchParts.push(`запрошенный поставщик ${requestedSupplier} не совпал с найденным поставщиком: ${inventoryPartyListOrUnknown(purchaseSummary.counterparties)}`);
}
if (requestedBuyer && saleRows.length > 0 && !buyerMatches) {
mismatchParts.push(`запрошенный покупатель ${requestedBuyer} не совпал с найденным покупателем: ${inventoryPartyListOrUnknown(saleSummary.counterparties)}`);
}
const directAnswerLine = mismatchParts.length > 0
? `Запрошенная цепочка по товару ${itemLabel} полностью не подтверждена: ${mismatchParts.join("; ")}.`
: purchaseSummary.counterparties.length === 1 && saleSummary.counterparties.length === 1
? `По товару ${itemLabel} цепочка поставки и продажи связана с поставщиком ${purchaseSummary.counterparties[0]} и покупателем ${saleSummary.counterparties[0]}.`
: `По товару ${itemLabel} цепочка поставки и продажи подтверждена частично или разнообразно: детали идут следом.`;
const lines = [directAnswerLine, "", "Подтверждение:"];
lines.push(`- Закупочных движений на 41.01: ${deps.formatNumberWithDots(purchaseRows.length)}.`);
lines.push(`- Движений выбытия со счета 41.01: ${deps.formatNumberWithDots(saleRows.length)}.`);
lines.push(`- Строк закупки на 41.01: ${deps.formatNumberWithDots(purchaseRows.length)}.`);
lines.push(`- Строк продажи со счета 41.01: ${deps.formatNumberWithDots(saleRows.length)}.`);
if (purchaseRows.length > 0 && saleRows.length > 0) {
lines.push("- В доступных данных найдены обе стороны цепочки: поступление и последующее выбытие.");
}
@@ -192,6 +192,29 @@ function readStateTransitionReasonCodes(input) {
.map((item) => toNonEmptyString(item))
.filter((item) => Boolean(item));
}
function readStringArray(value) {
return Array.isArray(value)
? value.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item))
: [];
}
function hasExactMatchedFactualAddressReply(input, entryPoint) {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
}
if (!hasEffectivelyFactualAddressReply(input)) {
return false;
}
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
const bindingStatus = toNonEmptyString(input.addressRuntimeMeta?.capability_binding_status);
const bindingViolations = readStringArray(input.addressRuntimeMeta?.capability_binding_violations);
return Boolean(mcpCallStatus === "matched_non_empty" &&
truthMode === "confirmed" &&
selectedRecipe?.startsWith("address_") &&
(bindingStatus === "bound" || bindingStatus === "bound_with_limits") &&
bindingViolations.length === 0);
}
function hasRuntimeAdjustedExactReply(input, entryPoint) {
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
return false;
@@ -332,6 +355,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
const matchedFactualAddressContinuationTarget = hasMatchedFactualAddressContinuationTarget(input, entryPoint);
const matchedFactualSuggestedIntentPivotTarget = hasMatchedFactualSuggestedIntentPivotTarget(input, entryPoint);
const fullConfirmedFactualAddressReply = hasFullConfirmedFactualAddressReply(input, entryPoint);
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
if (!entryPoint) {
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
@@ -363,6 +387,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
if (fullConfirmedFactualAddressReply) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_full_confirmed_factual_address_reply");
}
if (exactMatchedFactualAddressReply) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_exact_matched_factual_address_reply");
}
if (runtimeAdjustedExactReply) {
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_runtime_adjusted_exact_reply_over_stale_discovery_turn_meaning");
}
@@ -387,6 +414,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
!matchedFactualAddressContinuationTarget &&
!matchedFactualSuggestedIntentPivotTarget &&
!fullConfirmedFactualAddressReply &&
!exactMatchedFactualAddressReply &&
!runtimeAdjustedExactReply &&
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
@@ -112,8 +112,17 @@ function resolveAddressLaneProtectionArbitration(input) {
const semanticDeepInvestigationHintDetected = semanticGuardHints?.deep_investigation_signal_detected === true;
const semanticAggregateShapeDetected = semanticExtraction?.query_shape === "AGGREGATE_LOOKUP" ||
semanticExtraction?.aggregation_profile === "management_profile";
const exactSupportedIntentProtectedFromDeepPreference = Boolean(supportedAddressIntentDetected &&
resolvedIntent &&
ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(resolvedIntent) &&
semanticApplyCanonicalRecommended &&
(!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed));
const unsupportedAggregateFollowupOverride = Boolean(followupContext &&
llmContractMode === "unsupported" &&
(semanticAggregateShapeDetected || !semanticApplyCanonicalRecommended) &&
!exactSupportedIntentProtectedFromDeepPreference);
const followupSemanticOverrideToDeepAllowed = Boolean(followupContext &&
!supportedAddressIntentDetected &&
(!supportedAddressIntentDetected || unsupportedAggregateFollowupOverride) &&
(rootContextOnlyFollowup ||
llmContractMode === "unsupported" ||
semanticAggregateShapeDetected ||
@@ -127,11 +136,16 @@ function resolveAddressLaneProtectionArbitration(input) {
!deepAnalysisPreferenceDetected &&
!strictDeepInvestigationCueDetected &&
!semanticAggregateShapeDetected);
const unsupportedSpecificLlmIntent = Boolean(llmContractMode === "unsupported" &&
llmContractIntent &&
llmContractIntent !== "unknown");
const protectAddressLaneFromFallback = Boolean(supportedAddressRouteCandidateDetected &&
!deepAnalysisPreferenceDetected &&
(exactAddressIntentProtectedFromSemanticDeepHint ||
!semanticDeepInvestigationHintDetected ||
strictDeepInvestigationBypassAllowed));
(exactSupportedIntentProtectedFromDeepPreference ||
(!unsupportedSpecificLlmIntent &&
!deepAnalysisPreferenceDetected &&
(exactAddressIntentProtectedFromSemanticDeepHint ||
!semanticDeepInvestigationHintDetected ||
strictDeepInvestigationBypassAllowed))));
return {
supportedAddressIntentDetected,
supportedAddressRouteCandidateDetected,
@@ -139,6 +153,7 @@ function resolveAddressLaneProtectionArbitration(input) {
semanticAggregateShapeDetected,
followupSemanticOverrideToDeepAllowed,
exactAddressIntentProtectedFromSemanticDeepHint,
exactSupportedIntentProtectedFromDeepPreference,
protectAddressLaneFromFallback
};
}
@@ -282,7 +282,7 @@ exports.INVENTORY_CAPABILITY_CONTRACTS = [
entry_modes: ["root_entry", "root_followup", "clarification_resume"],
transitions: ["T1", "T2", "T7"],
requiresFocusObject: false,
requiredAnchors: ["supplier"],
requiredAnchors: [],
resultShape: "supplier_to_stock_item_overlap",
answerObjectShape: "inventory_supplier_overlap",
bundleReusePolicy: "none",