ARCH: добавить runtime authority смысла текущей реплики

This commit is contained in:
dctouch 2026-04-19 21:02:10 +03:00
parent 4e7974acc7
commit 153de1af7f
14 changed files with 643 additions and 18 deletions

View File

@ -1522,6 +1522,10 @@ function resolveAddressIntent(userMessage) {
const text = String(userMessage ?? "").trim().toLowerCase(); const text = String(userMessage ?? "").trim().toLowerCase();
const repairedText = repairLikelyUtf8Mojibake(text).trim().toLowerCase(); const repairedText = repairLikelyUtf8Mojibake(text).trim().toLowerCase();
const bridgeText = repairedText && repairedText !== text ? `${text} ${repairedText}` : text; const bridgeText = repairedText && repairedText !== text ? `${text} ${repairedText}` : text;
const turnNoiseNormalizedBridgeText = bridgeText
.replace(/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/giu, "$1\u043d\u0430\u043c")
.replace(/(^|[^\p{L}0-9_])\u043a\u0430\u043a\u0438\u0435\u043a(?=$|[^\p{L}0-9_])/giu, "$1\u043a\u0430\u043a\u0438\u0435");
const currentTurnBridgeText = turnNoiseNormalizedBridgeText !== bridgeText ? `${bridgeText} ${turnNoiseNormalizedBridgeText}` : bridgeText;
const hasLooseVatPayableBridge = /(?:\u043d\u0434\u0441|vat)/iu.test(text) && const hasLooseVatPayableBridge = /(?:\u043d\u0434\u0441|vat)/iu.test(text) &&
/(?:\u043a\u0430\u043a\u043e\u0439\s+\u043d\u0434\u0441\s+(?:(?:\u043d\u0430\u043c|(?:\u043c\u044b\s+)?\u0434\u043e\u043b\u0436\u043d\u044b)\s+)?(?:\u043d\u0430\u0434\u043e|\u043d\u0443\u0436\u043d\u043e|\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e)|(?:\u043d\u0430\u043c|\u043c\u044b\s+)?\u043d\u0430\u0434\u043e\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|(?:\u043d\u0430\u043c|\u043c\u044b\s+)?\u043d\u0443\u0436\u043d\u043e\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|\u043d\u0434\u0441\s+\u043a\s+\u0443\u043f\u043b\u0430\u0442\u0435)/iu.test(text) && /(?:\u043a\u0430\u043a\u043e\u0439\s+\u043d\u0434\u0441\s+(?:(?:\u043d\u0430\u043c|(?:\u043c\u044b\s+)?\u0434\u043e\u043b\u0436\u043d\u044b)\s+)?(?:\u043d\u0430\u0434\u043e|\u043d\u0443\u0436\u043d\u043e|\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e)|(?:\u043d\u0430\u043c|\u043c\u044b\s+)?\u043d\u0430\u0434\u043e\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|(?:\u043d\u0430\u043c|\u043c\u044b\s+)?\u043d\u0443\u0436\u043d\u043e\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+(?:\u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c|\u0441\u0433\u0440\u0443\u0437\u0438\u0442\u044c)|\u043d\u0434\u0441\s+\u043a\s+\u0443\u043f\u043b\u0430\u0442\u0435)/iu.test(text) &&
/(?:\u0437\u0430\s+(?:\d{4}|(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?)|\u043d\u0430\s+(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?|\u0432\s+(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?|\b[1-4]\s*(?:\u043a\u0432\u0430\u0440\u0442\u0430\u043b|\u043a\u0432\.?)\b)/iu.test(text); /(?:\u0437\u0430\s+(?:\d{4}|(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?)|\u043d\u0430\s+(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?|\u0432\s+(?:\u044f\u043d\u0432\u0430\u0440|\u0444\u0435\u0432\u0440\u0430\u043b|\u043c\u0430\u0440\u0442|\u0430\u043f\u0440\u0435\u043b|\u043c\u0430[\u0439\u044f]|\u0438\u044e\u043d|\u0438\u044e\u043b|\u0430\u0432\u0433\u0443\u0441\u0442|\u0441\u0435\u043d\u0442\u044f\u0431\u0440|\u043e\u043a\u0442\u044f\u0431\u0440|\u043d\u043e\u044f\u0431\u0440|\u0434\u0435\u043a\u0430\u0431\u0440)\S*(?:\s+(?:19|20)\d{2})?|\b[1-4]\s*(?:\u043a\u0432\u0430\u0440\u0442\u0430\u043b|\u043a\u0432\.?)\b)/iu.test(text);
@ -1532,20 +1536,24 @@ function resolveAddressIntent(userMessage) {
reasons: ["vat_liability_colloquial_bridge_signal_detected"] reasons: ["vat_liability_colloquial_bridge_signal_detected"]
}; };
} }
const hasExplicitReceivablesSnapshotBridge = /(?:\u043d\u0430\u043c\s+\u043a\u0442\u043e-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a\w+\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043d\u0430\u043c|receivables?)/iu.test(text); const hasExplicitReceivablesSnapshotBridge = /(?:\u043d\u0430\u043c\s+\u043a\u0442\u043e-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a\w+\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043d\u0430\u043c|receivables?)/iu.test(currentTurnBridgeText);
if (hasExplicitReceivablesSnapshotBridge) { if (hasExplicitReceivablesSnapshotBridge) {
return { return {
intent: "receivables_confirmed_as_of_date", intent: "receivables_confirmed_as_of_date",
confidence: "high", confidence: "high",
reasons: ["receivables_snapshot_bridge_signal_detected"] reasons: currentTurnBridgeText !== bridgeText
? ["receivables_snapshot_bridge_signal_detected", "current_turn_noise_normalized"]
: ["receivables_snapshot_bridge_signal_detected"]
}; };
} }
const hasExplicitPayablesSnapshotBridge = /(?:\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|payables?)/iu.test(text); const hasExplicitPayablesSnapshotBridge = /(?:\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|payables?)/iu.test(currentTurnBridgeText);
if (hasExplicitPayablesSnapshotBridge) { if (hasExplicitPayablesSnapshotBridge) {
return { return {
intent: "payables_confirmed_as_of_date", intent: "payables_confirmed_as_of_date",
confidence: "high", confidence: "high",
reasons: ["payables_snapshot_bridge_signal_detected"] reasons: currentTurnBridgeText !== bridgeText
? ["payables_snapshot_bridge_signal_detected", "current_turn_noise_normalized"]
: ["payables_snapshot_bridge_signal_detected"]
}; };
} }
const hasDirectInventoryAgingBridge = /(?:\u043e\u0447\u0435\u043d\u044c\s+\u0434\u0430\u0432\u043d\u043e|\u0434\u0430\u0432\u043d\u043e\s+\u043a\u0443\u043f\u043b|\u0434\u0430\u0432\u043d\u043e\s+\u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442|\u0441\u0442\u0430\u0440(?:\u044b\u0435|\u044b\u043c|\u044b\u0445)?\s+\u0437\u0430\u043a\u0443\u043f|\u0441\u0442\u0430\u0440(?:\u044b\u0439|\u0430\u044f|\u043e\u0435)?\s+\u0442\u043e\u0432\u0430\u0440|old\s+stock|old\s+purchase|very\s+old\s+stock|aging\s+by\s+purchase\s+date)/iu.test(bridgeText); const hasDirectInventoryAgingBridge = /(?:\u043e\u0447\u0435\u043d\u044c\s+\u0434\u0430\u0432\u043d\u043e|\u0434\u0430\u0432\u043d\u043e\s+\u043a\u0443\u043f\u043b|\u0434\u0430\u0432\u043d\u043e\s+\u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442|\u0441\u0442\u0430\u0440(?:\u044b\u0435|\u044b\u043c|\u044b\u0445)?\s+\u0437\u0430\u043a\u0443\u043f|\u0441\u0442\u0430\u0440(?:\u044b\u0439|\u0430\u044f|\u043e\u0435)?\s+\u0442\u043e\u0432\u0430\u0440|old\s+stock|old\s+purchase|very\s+old\s+stock|aging\s+by\s+purchase\s+date)/iu.test(bridgeText);

View File

@ -143,7 +143,7 @@ function resolveAddressLaneProtectionArbitration(input) {
}; };
} }
function createAssistantRoutePolicy(deps) { function createAssistantRoutePolicy(deps) {
const { repairAddressMojibake, findLastGroundedAddressAnswerDebug, findLastOrganizationClarificationAddressDebug, mergeKnownOrganizations, normalizeOrganizationScopeValue, resolveOrganizationSelectionFromMessage, resolveMetaSignalSet, resolveHardMetaMode, isMetaFollowupOverGroundedAnswer, isAnswerInspectionFollowupOverGroundedAnswer, hasDataRetrievalRequestSignal, hasOrganizationFactLookupSignal, hasOrganizationFactFollowupSignal, hasAggregateBusinessAnalyticsSignal, hasStandaloneAddressTopicSignal, hasOpenContractsAddressSignal, detectAddressQuestionMode, resolveAddressIntent, toNonEmptyString, hasStrictDeepInvestigationCue, hasStrongDataIntentSignal, hasAccountingSignal, hasDangerOrCoercionSignal, hasAddressFollowupContextSignal, hasShortDebtMirrorFollowupSignal, isInventorySelectedObjectIntent, hasShortInventoryObjectFollowupSignal, isGroundedInventoryContextDebug, resolveRouteMemorySignals, findLastAddressAssistantItem, resolveAddressToolGateDecision: resolveAddressToolGateDecisionOverride, hasAddressLlmPreDecomposeCandidate, hasSameDateAccountFollowupSignalForPredecompose, hasLooseAllTimeAddressLookupSignal, hasDeepAnalysisPreferenceSignal, hasDirectDeepAnalysisSignal, shouldEmitOrganizationSelectionReply, compactWhitespace, hasDeepSessionContinuationSignal, resolveLivingAssistantModeDecision, resolveProviderExecutionState } = deps; const { repairAddressMojibake, findLastGroundedAddressAnswerDebug, findLastOrganizationClarificationAddressDebug, mergeKnownOrganizations, normalizeOrganizationScopeValue, resolveOrganizationSelectionFromMessage, resolveMetaSignalSet, resolveHardMetaMode, isMetaFollowupOverGroundedAnswer, isAnswerInspectionFollowupOverGroundedAnswer, hasDataRetrievalRequestSignal, hasOrganizationFactLookupSignal, hasOrganizationFactFollowupSignal, hasAggregateBusinessAnalyticsSignal, hasStandaloneAddressTopicSignal, hasOpenContractsAddressSignal, detectAddressQuestionMode, resolveAddressIntent, toNonEmptyString, hasStrictDeepInvestigationCue, hasStrongDataIntentSignal, hasAccountingSignal, hasDangerOrCoercionSignal, hasAddressFollowupContextSignal, hasShortDebtMirrorFollowupSignal, isInventorySelectedObjectIntent, hasShortInventoryObjectFollowupSignal, isGroundedInventoryContextDebug, resolveRouteMemorySignals, findLastAddressAssistantItem, resolveAddressToolGateDecision: resolveAddressToolGateDecisionOverride, hasAddressLlmPreDecomposeCandidate, hasSameDateAccountFollowupSignalForPredecompose, hasLooseAllTimeAddressLookupSignal, hasDeepAnalysisPreferenceSignal, hasDirectDeepAnalysisSignal, shouldEmitOrganizationSelectionReply, compactWhitespace, hasDeepSessionContinuationSignal, resolveLivingAssistantModeDecision, resolveProviderExecutionState, resolveAssistantTurnMeaning } = deps;
function resolveBaseAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) { function resolveBaseAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) {
const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? "")); const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? ""));
const rawMessageForGate = String(rawUserMessage ?? addressInputMessage ?? ""); const rawMessageForGate = String(rawUserMessage ?? addressInputMessage ?? "");
@ -375,8 +375,30 @@ function createAssistantRoutePolicy(deps) {
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw; const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
const intentResolution = resolveAddressIntent(modeSample); const intentResolution = resolveAddressIntent(modeSample);
const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage); const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage);
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent); const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const assistantTurnMeaning = typeof resolveAssistantTurnMeaning === "function"
? resolveAssistantTurnMeaning({
rawUserMessage,
effectiveAddressUserMessage,
repairedRawUserMessage,
repairedEffectiveAddressUserMessage,
llmPreDecomposeMeta
})
: null;
const turnMeaningIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
const baseResolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
const resolvedIntentResolution = turnMeaningIntentCandidate &&
baseResolvedIntentResolution.intent === "unknown" &&
ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(turnMeaningIntentCandidate)
? {
intent: turnMeaningIntentCandidate,
confidence: assistantTurnMeaning?.meaning_confidence === "high" ? "high" : "medium",
reasons: [
"assistant_turn_meaning_intent_recovery",
...(Array.isArray(assistantTurnMeaning?.reason_codes) ? assistantTurnMeaning.reason_codes : [])
]
}
: baseResolvedIntentResolution;
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason); const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
const providerExecution = resolveProviderExecutionState({ const providerExecution = resolveProviderExecutionState({
useMock, useMock,
@ -1098,6 +1120,7 @@ function createAssistantRoutePolicy(deps) {
schema_version: "assistant_orchestration_contract_v1", schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: null, hard_meta_mode: null,
provider_execution: providerExecution, provider_execution: providerExecution,
assistant_turn_meaning: assistantTurnMeaning,
address_mode: resolvedModeDetection.mode, address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence, address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent, address_intent: resolvedIntentResolution.intent,

View File

@ -74,6 +74,7 @@ const assistantMetaFollowupPolicy_1 = __importStar(require("./assistantMetaFollo
const assistantMemoryRecapPolicy_1 = __importStar(require("./assistantMemoryRecapPolicy")); const assistantMemoryRecapPolicy_1 = __importStar(require("./assistantMemoryRecapPolicy"));
const assistantContinuityPolicy_1 = __importStar(require("./assistantContinuityPolicy")); const assistantContinuityPolicy_1 = __importStar(require("./assistantContinuityPolicy"));
const assistantProviderExecutionPolicy_1 = __importStar(require("./assistantProviderExecutionPolicy")); const assistantProviderExecutionPolicy_1 = __importStar(require("./assistantProviderExecutionPolicy"));
const assistantTurnMeaningPolicy_1 = __importStar(require("./assistantTurnMeaningPolicy"));
const assistantRoutePolicy_1 = __importStar(require("./assistantRoutePolicy")); const assistantRoutePolicy_1 = __importStar(require("./assistantRoutePolicy"));
const assistantTransitionPolicy_1 = __importStar(require("./assistantTransitionPolicy")); const assistantTransitionPolicy_1 = __importStar(require("./assistantTransitionPolicy"));
const assistantOrganizationScopeRuntimeAdapter_1 = __importStar(require("./assistantOrganizationScopeRuntimeAdapter")); const assistantOrganizationScopeRuntimeAdapter_1 = __importStar(require("./assistantOrganizationScopeRuntimeAdapter"));
@ -4057,6 +4058,12 @@ const assistantMemoryRecapPolicy = (0, assistantMemoryRecapPolicy_1.createAssist
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal, hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
isGroundedInventoryContextDebug isGroundedInventoryContextDebug
}); });
const assistantTurnMeaningPolicy = (0, assistantTurnMeaningPolicy_1.createAssistantTurnMeaningPolicy)({
compactWhitespace,
repairAddressMojibake,
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
toNonEmptyString
});
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
repairAddressMojibake, repairAddressMojibake,
findLastGroundedAddressAnswerDebug, findLastGroundedAddressAnswerDebug,
@ -4096,7 +4103,8 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
compactWhitespace, compactWhitespace,
hasDeepSessionContinuationSignal, hasDeepSessionContinuationSignal,
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision, resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision,
resolveProviderExecutionState: assistantProviderExecutionPolicy.resolveProviderExecutionState resolveProviderExecutionState: assistantProviderExecutionPolicy.resolveProviderExecutionState,
resolveAssistantTurnMeaning: assistantTurnMeaningPolicy.resolveAssistantTurnMeaning
}); });
const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({ const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({
compactWhitespace, compactWhitespace,
@ -4132,7 +4140,8 @@ const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistan
buildRootScopedCarryoverFilters, buildRootScopedCarryoverFilters,
inferDisplayedEntityTypeFromIntent, inferDisplayedEntityTypeFromIntent,
extractDisplayedAddressEntityCandidates, extractDisplayedAddressEntityCandidates,
resolveDisplayedAddressEntityMention resolveDisplayedAddressEntityMention,
resolveAssistantTurnMeaning: assistantTurnMeaningPolicy.resolveAssistantTurnMeaning
}); });
const assistantDataScopePolicy = (0, assistantDataScopePolicy_1.createAssistantDataScopePolicy)({ const assistantDataScopePolicy = (0, assistantDataScopePolicy_1.createAssistantDataScopePolicy)({
activeMcpChannel: config_1.ASSISTANT_MCP_CHANNEL, activeMcpChannel: config_1.ASSISTANT_MCP_CHANNEL,

View File

@ -288,6 +288,16 @@ function createAssistantTransitionPolicy(deps) {
if (rawCapabilityMetaQuery && !rawDataRetrievalSignal) { if (rawCapabilityMetaQuery && !rawDataRetrievalSignal) {
return null; return null;
} }
const assistantTurnMeaning = typeof deps.resolveAssistantTurnMeaning === "function"
? deps.resolveAssistantTurnMeaning({
rawUserMessage: userMessage,
effectiveAddressUserMessage: alternateMessage ?? userMessage,
llmPreDecomposeMeta
})
: null;
if (assistantTurnMeaning?.stale_replay_forbidden === true) {
return null;
}
const latestAddressItem = deps.findLastAddressAssistantItem(items); const latestAddressItem = deps.findLastAddressAssistantItem(items);
const previousAddressItem = (latestAddressItem && isUsableFollowupSourceDebug(latestAddressItem?.debug) const previousAddressItem = (latestAddressItem && isUsableFollowupSourceDebug(latestAddressItem?.debug)
? latestAddressItem ? latestAddressItem
@ -425,12 +435,15 @@ function createAssistantTransitionPolicy(deps) {
const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage) const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage)
? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent ? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent
: null; : null;
const assistantTurnMeaningIntent = deps.toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
const explicitIntent = llmExplicitIntent && llmExplicitIntent !== "unknown" const explicitIntent = llmExplicitIntent && llmExplicitIntent !== "unknown"
? llmExplicitIntent ? llmExplicitIntent
: resolvedPrimaryIntent && resolvedPrimaryIntent !== "unknown" : resolvedPrimaryIntent && resolvedPrimaryIntent !== "unknown"
? resolvedPrimaryIntent ? resolvedPrimaryIntent
: resolvedAlternateIntent && resolvedAlternateIntent !== "unknown" : resolvedAlternateIntent && resolvedAlternateIntent !== "unknown"
? resolvedAlternateIntent ? resolvedAlternateIntent
: assistantTurnMeaningIntent && assistantTurnMeaningIntent !== "unknown"
? assistantTurnMeaningIntent
: null; : null;
const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent); const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent);
const explicitIntentFamily = deps.resolveAddressIntentFamily(explicitIntent) ?? const explicitIntentFamily = deps.resolveAddressIntentFamily(explicitIntent) ??

View File

@ -0,0 +1,180 @@
"use strict";
// @ts-nocheck
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAssistantTurnMeaningPolicy = createAssistantTurnMeaningPolicy;
const SUPPORTED_ADDRESS_INTENTS = new Set([
"receivables_confirmed_as_of_date",
"payables_confirmed_as_of_date",
"list_documents_by_counterparty",
"inventory_on_hand_as_of_date"
]);
function fallbackCompactWhitespace(value) {
return String(value ?? "").replace(/\s+/g, " ").trim();
}
function normalizeTurnText(value, deps) {
const compactWhitespace = typeof deps?.compactWhitespace === "function" ? deps.compactWhitespace : fallbackCompactWhitespace;
const repaired = typeof deps?.repairAddressMojibake === "function"
? deps.repairAddressMojibake(String(value ?? ""))
: String(value ?? "");
return compactWhitespace(repaired.toLowerCase())
.replace(/\u0451/gu, "\u0435")
.replace(/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/giu, "$1\u043d\u0430\u043c")
.replace(/(^|[^\p{L}0-9_])\u043a\u0430\u043a\u0438\u0435\u043a(?=$|[^\p{L}0-9_])/giu, "$1\u043a\u0430\u043a\u0438\u0435");
}
function toNonEmptyString(value, deps) {
if (typeof deps?.toNonEmptyString === "function") {
return deps.toNonEmptyString(value);
}
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function detectSupportedIntent(text, deps) {
const resolved = typeof deps?.resolveAddressIntent === "function" ? deps.resolveAddressIntent(text) : null;
const resolverIntent = toNonEmptyString(resolved?.intent, deps);
if (resolverIntent && resolverIntent !== "unknown" && SUPPORTED_ADDRESS_INTENTS.has(resolverIntent)) {
return {
intent: resolverIntent,
confidence: toNonEmptyString(resolved?.confidence, deps) ?? "medium",
reason: "address_intent_resolver_current_turn_signal"
};
}
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
return {
intent: "receivables_confirmed_as_of_date",
confidence: "high",
reason: "receivables_current_turn_meaning_signal"
};
}
if (/(?:\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u043a\u043e\u043c\u0443\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d|\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440|\bpayables?\b)/iu.test(text)) {
return {
intent: "payables_confirmed_as_of_date",
confidence: "high",
reason: "payables_current_turn_meaning_signal"
};
}
if (/(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0434\u043e\u043a\u0438|docs?|documents?)/iu.test(text) && /(?:\u043f\u043e|by)\s+[\p{L}0-9._-]{2,}/iu.test(text)) {
return {
intent: "list_documents_by_counterparty",
confidence: "medium",
reason: "counterparty_documents_current_turn_signal"
};
}
if (/(?:\u043e\u0441\u0442\u0430\u0442|\u0441\u043a\u043b\u0430\u0434|inventory|stock)/iu.test(text)) {
return {
intent: "inventory_on_hand_as_of_date",
confidence: "medium",
reason: "inventory_snapshot_current_turn_signal"
};
}
return null;
}
function detectCounterpartyTurnoverFamily(text) {
const hasTurnoverCue = /(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a|\u0434\u043e\u0445\u043e\u0434|turnover|revenue)/iu.test(text);
if (!hasTurnoverCue) {
return null;
}
const explicitEntityMatch = text.match(/(?:\u043f\u043e|by|for)?\s*([\p{L}0-9._-]{2,})\s*$/iu);
const rawEntity = explicitEntityMatch?.[1] ?? null;
const ignored = new Set([
"\u043e\u0431\u043e\u0440\u043e\u0442",
"\u0432\u044b\u0440\u0443\u0447\u043a\u0430",
"\u0434\u043e\u0445\u043e\u0434",
"\u0431\u044b\u043b",
"\u0431\u044b\u043b\u0430",
"turnover",
"revenue"
]);
const entity = rawEntity && !ignored.has(rawEntity) ? rawEntity : null;
return {
family: "counterparty_value_or_turnover",
entity
};
}
function buildEntityCandidates(counterpartyTurnover) {
if (!counterpartyTurnover?.entity) {
return [];
}
return [
{
type: "counterparty",
value: counterpartyTurnover.entity,
source: "current_turn_loose_entity_tail"
}
];
}
function createAssistantTurnMeaningPolicy(deps = {}) {
function resolveAssistantTurnMeaning(input = {}) {
const rawMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
const effectiveMessage = String(input?.effectiveAddressUserMessage ?? rawMessage);
const rawText = normalizeTurnText(rawMessage, deps);
const effectiveText = normalizeTurnText(effectiveMessage, deps);
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
const supportedIntent = detectSupportedIntent(joinedText, deps);
const counterpartyTurnover = detectCounterpartyTurnoverFamily(joinedText);
const llmIntent = toNonEmptyString(input?.llmPreDecomposeMeta?.predecomposeContract?.intent, deps);
const explicitIntentCandidate = supportedIntent?.intent ?? (llmIntent && llmIntent !== "unknown" ? llmIntent : null);
const unsupportedFamily = !explicitIntentCandidate && counterpartyTurnover?.family ? counterpartyTurnover.family : null;
const reasonCodes = [];
if (supportedIntent?.reason) {
reasonCodes.push(supportedIntent.reason);
}
if (counterpartyTurnover?.family) {
reasonCodes.push("counterparty_turnover_current_turn_signal");
}
if (rawText !== normalizeTurnText(rawMessage, { ...deps, repairAddressMojibake: (value) => String(value ?? "") })) {
reasonCodes.push("mojibake_repair_applied");
}
if (rawText.includes("\u043d\u0430\u043c") &&
/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/iu.test(String(rawMessage ?? ""))) {
reasonCodes.push("known_turn_typo_normalized");
}
const askedDomainFamily = explicitIntentCandidate?.startsWith("receivables_")
? "receivables"
: explicitIntentCandidate?.startsWith("payables_")
? "payables"
: explicitIntentCandidate?.startsWith("inventory_")
? "inventory"
: explicitIntentCandidate?.includes("counterparty")
? "counterparty"
: counterpartyTurnover?.family
? "counterparty"
: null;
const askedActionFamily = explicitIntentCandidate === "receivables_confirmed_as_of_date" ||
explicitIntentCandidate === "payables_confirmed_as_of_date" ||
explicitIntentCandidate === "inventory_on_hand_as_of_date"
? "confirmed_snapshot"
: explicitIntentCandidate === "list_documents_by_counterparty"
? "list_documents"
: counterpartyTurnover?.family
? "counterparty_value_or_turnover"
: null;
const staleReplayForbidden = Boolean(unsupportedFamily || (counterpartyTurnover?.entity && !explicitIntentCandidate));
return {
schema_version: "assistant_turn_meaning_v1",
raw_message: rawMessage,
effective_message: effectiveMessage,
normalized_raw_message: rawText,
normalized_effective_message: effectiveText,
asked_domain_family: askedDomainFamily,
asked_action_family: askedActionFamily,
explicit_intent_candidate: explicitIntentCandidate,
explicit_entity_candidates: buildEntityCandidates(counterpartyTurnover),
meaning_confidence: supportedIntent?.confidence ?? (counterpartyTurnover?.family ? "medium" : "low"),
intent_override_strength: explicitIntentCandidate
? "explicit_current_turn_intent"
: staleReplayForbidden
? "explicit_new_action_or_entity"
: "none",
carryover_budget: staleReplayForbidden ? "none" : explicitIntentCandidate ? "matching_family_only" : "normal",
unsupported_but_understood_family: unsupportedFamily,
stale_replay_forbidden: staleReplayForbidden,
reason_codes: reasonCodes
};
}
return {
resolveAssistantTurnMeaning
};
}

View File

@ -1904,6 +1904,11 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
const text = String(userMessage ?? "").trim().toLowerCase(); const text = String(userMessage ?? "").trim().toLowerCase();
const repairedText = repairLikelyUtf8Mojibake(text).trim().toLowerCase(); const repairedText = repairLikelyUtf8Mojibake(text).trim().toLowerCase();
const bridgeText = repairedText && repairedText !== text ? `${text} ${repairedText}` : text; const bridgeText = repairedText && repairedText !== text ? `${text} ${repairedText}` : text;
const turnNoiseNormalizedBridgeText = bridgeText
.replace(/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/giu, "$1\u043d\u0430\u043c")
.replace(/(^|[^\p{L}0-9_])\u043a\u0430\u043a\u0438\u0435\u043a(?=$|[^\p{L}0-9_])/giu, "$1\u043a\u0430\u043a\u0438\u0435");
const currentTurnBridgeText =
turnNoiseNormalizedBridgeText !== bridgeText ? `${bridgeText} ${turnNoiseNormalizedBridgeText}` : bridgeText;
const hasLooseVatPayableBridge = const hasLooseVatPayableBridge =
/(?:\u043d\u0434\u0441|vat)/iu.test(text) && /(?:\u043d\u0434\u0441|vat)/iu.test(text) &&
@ -1923,25 +1928,31 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
const hasExplicitReceivablesSnapshotBridge = const hasExplicitReceivablesSnapshotBridge =
/(?:\u043d\u0430\u043c\s+\u043a\u0442\u043e-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a\w+\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043d\u0430\u043c|receivables?)/iu.test( /(?:\u043d\u0430\u043c\s+\u043a\u0442\u043e-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436\u0435\u043d|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a\w+\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043d\u0430\u043c|receivables?)/iu.test(
text currentTurnBridgeText
); );
if (hasExplicitReceivablesSnapshotBridge) { if (hasExplicitReceivablesSnapshotBridge) {
return { return {
intent: "receivables_confirmed_as_of_date", intent: "receivables_confirmed_as_of_date",
confidence: "high", confidence: "high",
reasons: ["receivables_snapshot_bridge_signal_detected"] reasons:
currentTurnBridgeText !== bridgeText
? ["receivables_snapshot_bridge_signal_detected", "current_turn_noise_normalized"]
: ["receivables_snapshot_bridge_signal_detected"]
}; };
} }
const hasExplicitPayablesSnapshotBridge = const hasExplicitPayablesSnapshotBridge =
/(?:\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|payables?)/iu.test( /(?:\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b\s+\u043a\u043e\u043c\u0443\u0442\u043e\s+\u0434\u0435\u043d\u0435\u0433|\u043c\u044b\s+\u043a\u043e\u043c\u0443-\u0442\u043e\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d\u044b|\u0435\u0441\u0442\u044c\s+\u043b\u0438\s+\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|\u0437\u0430\u0434\u043e\u043b\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\s+\u043f\u0435\u0440\u0435\u0434\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c\u0438|payables?)/iu.test(
text currentTurnBridgeText
); );
if (hasExplicitPayablesSnapshotBridge) { if (hasExplicitPayablesSnapshotBridge) {
return { return {
intent: "payables_confirmed_as_of_date", intent: "payables_confirmed_as_of_date",
confidence: "high", confidence: "high",
reasons: ["payables_snapshot_bridge_signal_detected"] reasons:
currentTurnBridgeText !== bridgeText
? ["payables_snapshot_bridge_signal_detected", "current_turn_noise_normalized"]
: ["payables_snapshot_bridge_signal_detected"]
}; };
} }

View File

@ -222,7 +222,8 @@ export function createAssistantRoutePolicy(deps) {
compactWhitespace, compactWhitespace,
hasDeepSessionContinuationSignal, hasDeepSessionContinuationSignal,
resolveLivingAssistantModeDecision, resolveLivingAssistantModeDecision,
resolveProviderExecutionState resolveProviderExecutionState,
resolveAssistantTurnMeaning
} = deps; } = deps;
function resolveBaseAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) { function resolveBaseAddressToolGateDecision(addressInputMessage, followupContext, llmPreDecomposeMeta = null, rawUserMessage = null) {
const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? "")); const repairedInputMessage = repairAddressMojibake(String(addressInputMessage ?? ""));
@ -455,8 +456,32 @@ export function createAssistantRoutePolicy(deps) {
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw; const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
const intentResolution = resolveAddressIntent(modeSample); const intentResolution = resolveAddressIntent(modeSample);
const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage); const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage);
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent); const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const assistantTurnMeaning =
typeof resolveAssistantTurnMeaning === "function"
? resolveAssistantTurnMeaning({
rawUserMessage,
effectiveAddressUserMessage,
repairedRawUserMessage,
repairedEffectiveAddressUserMessage,
llmPreDecomposeMeta
})
: null;
const turnMeaningIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
const baseResolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
const resolvedIntentResolution =
turnMeaningIntentCandidate &&
baseResolvedIntentResolution.intent === "unknown" &&
ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(turnMeaningIntentCandidate)
? {
intent: turnMeaningIntentCandidate,
confidence: assistantTurnMeaning?.meaning_confidence === "high" ? "high" : "medium",
reasons: [
"assistant_turn_meaning_intent_recovery",
...(Array.isArray(assistantTurnMeaning?.reason_codes) ? assistantTurnMeaning.reason_codes : [])
]
}
: baseResolvedIntentResolution;
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason); const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
const providerExecution = resolveProviderExecutionState({ const providerExecution = resolveProviderExecutionState({
useMock, useMock,
@ -1178,6 +1203,7 @@ export function createAssistantRoutePolicy(deps) {
schema_version: "assistant_orchestration_contract_v1", schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: null, hard_meta_mode: null,
provider_execution: providerExecution, provider_execution: providerExecution,
assistant_turn_meaning: assistantTurnMeaning,
address_mode: resolvedModeDetection.mode, address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence, address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent, address_intent: resolvedIntentResolution.intent,

View File

@ -28,6 +28,7 @@ import * as assistantMetaFollowupPolicy_1 from "./assistantMetaFollowupPolicy";
import * as assistantMemoryRecapPolicy_1 from "./assistantMemoryRecapPolicy"; import * as assistantMemoryRecapPolicy_1 from "./assistantMemoryRecapPolicy";
import * as assistantContinuityPolicy_1 from "./assistantContinuityPolicy"; import * as assistantContinuityPolicy_1 from "./assistantContinuityPolicy";
import * as assistantProviderExecutionPolicy_1 from "./assistantProviderExecutionPolicy"; import * as assistantProviderExecutionPolicy_1 from "./assistantProviderExecutionPolicy";
import * as assistantTurnMeaningPolicy_1 from "./assistantTurnMeaningPolicy";
import * as assistantRoutePolicy_1 from "./assistantRoutePolicy"; import * as assistantRoutePolicy_1 from "./assistantRoutePolicy";
import * as assistantTransitionPolicy_1 from "./assistantTransitionPolicy"; import * as assistantTransitionPolicy_1 from "./assistantTransitionPolicy";
import * as assistantOrganizationScopeRuntimeAdapter_1 from "./assistantOrganizationScopeRuntimeAdapter"; import * as assistantOrganizationScopeRuntimeAdapter_1 from "./assistantOrganizationScopeRuntimeAdapter";
@ -4014,6 +4015,12 @@ const assistantMemoryRecapPolicy = (0, assistantMemoryRecapPolicy_1.createAssist
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal, hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
isGroundedInventoryContextDebug isGroundedInventoryContextDebug
}); });
const assistantTurnMeaningPolicy = (0, assistantTurnMeaningPolicy_1.createAssistantTurnMeaningPolicy)({
compactWhitespace,
repairAddressMojibake,
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
toNonEmptyString
});
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
repairAddressMojibake, repairAddressMojibake,
findLastGroundedAddressAnswerDebug, findLastGroundedAddressAnswerDebug,
@ -4053,7 +4060,8 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
compactWhitespace, compactWhitespace,
hasDeepSessionContinuationSignal, hasDeepSessionContinuationSignal,
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision, resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision,
resolveProviderExecutionState: assistantProviderExecutionPolicy.resolveProviderExecutionState resolveProviderExecutionState: assistantProviderExecutionPolicy.resolveProviderExecutionState,
resolveAssistantTurnMeaning: assistantTurnMeaningPolicy.resolveAssistantTurnMeaning
}); });
const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({ const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({
compactWhitespace, compactWhitespace,
@ -4089,7 +4097,8 @@ const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistan
buildRootScopedCarryoverFilters, buildRootScopedCarryoverFilters,
inferDisplayedEntityTypeFromIntent, inferDisplayedEntityTypeFromIntent,
extractDisplayedAddressEntityCandidates, extractDisplayedAddressEntityCandidates,
resolveDisplayedAddressEntityMention resolveDisplayedAddressEntityMention,
resolveAssistantTurnMeaning: assistantTurnMeaningPolicy.resolveAssistantTurnMeaning
}); });
const assistantDataScopePolicy = (0, assistantDataScopePolicy_1.createAssistantDataScopePolicy)({ const assistantDataScopePolicy = (0, assistantDataScopePolicy_1.createAssistantDataScopePolicy)({
activeMcpChannel: config_1.ASSISTANT_MCP_CHANNEL, activeMcpChannel: config_1.ASSISTANT_MCP_CHANNEL,

View File

@ -381,6 +381,17 @@ export function createAssistantTransitionPolicy(deps) {
if (rawCapabilityMetaQuery && !rawDataRetrievalSignal) { if (rawCapabilityMetaQuery && !rawDataRetrievalSignal) {
return null; return null;
} }
const assistantTurnMeaning =
typeof deps.resolveAssistantTurnMeaning === "function"
? deps.resolveAssistantTurnMeaning({
rawUserMessage: userMessage,
effectiveAddressUserMessage: alternateMessage ?? userMessage,
llmPreDecomposeMeta
})
: null;
if (assistantTurnMeaning?.stale_replay_forbidden === true) {
return null;
}
const latestAddressItem = deps.findLastAddressAssistantItem(items); const latestAddressItem = deps.findLastAddressAssistantItem(items);
const previousAddressItem = const previousAddressItem =
(latestAddressItem && isUsableFollowupSourceDebug(latestAddressItem?.debug) (latestAddressItem && isUsableFollowupSourceDebug(latestAddressItem?.debug)
@ -566,6 +577,7 @@ export function createAssistantTransitionPolicy(deps) {
const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage) const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage)
? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent ? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent
: null; : null;
const assistantTurnMeaningIntent = deps.toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
const explicitIntent = const explicitIntent =
llmExplicitIntent && llmExplicitIntent !== "unknown" llmExplicitIntent && llmExplicitIntent !== "unknown"
? llmExplicitIntent ? llmExplicitIntent
@ -573,6 +585,8 @@ export function createAssistantTransitionPolicy(deps) {
? resolvedPrimaryIntent ? resolvedPrimaryIntent
: resolvedAlternateIntent && resolvedAlternateIntent !== "unknown" : resolvedAlternateIntent && resolvedAlternateIntent !== "unknown"
? resolvedAlternateIntent ? resolvedAlternateIntent
: assistantTurnMeaningIntent && assistantTurnMeaningIntent !== "unknown"
? assistantTurnMeaningIntent
: null; : null;
const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent); const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent);
const explicitIntentFamily = const explicitIntentFamily =

View File

@ -0,0 +1,192 @@
// @ts-nocheck
const SUPPORTED_ADDRESS_INTENTS = new Set([
"receivables_confirmed_as_of_date",
"payables_confirmed_as_of_date",
"list_documents_by_counterparty",
"inventory_on_hand_as_of_date"
]);
function fallbackCompactWhitespace(value) {
return String(value ?? "").replace(/\s+/g, " ").trim();
}
function normalizeTurnText(value, deps) {
const compactWhitespace = typeof deps?.compactWhitespace === "function" ? deps.compactWhitespace : fallbackCompactWhitespace;
const repaired =
typeof deps?.repairAddressMojibake === "function"
? deps.repairAddressMojibake(String(value ?? ""))
: String(value ?? "");
return compactWhitespace(repaired.toLowerCase())
.replace(/\u0451/gu, "\u0435")
.replace(/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/giu, "$1\u043d\u0430\u043c")
.replace(/(^|[^\p{L}0-9_])\u043a\u0430\u043a\u0438\u0435\u043a(?=$|[^\p{L}0-9_])/giu, "$1\u043a\u0430\u043a\u0438\u0435");
}
function toNonEmptyString(value, deps) {
if (typeof deps?.toNonEmptyString === "function") {
return deps.toNonEmptyString(value);
}
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function detectSupportedIntent(text, deps) {
const resolved = typeof deps?.resolveAddressIntent === "function" ? deps.resolveAddressIntent(text) : null;
const resolverIntent = toNonEmptyString(resolved?.intent, deps);
if (resolverIntent && resolverIntent !== "unknown" && SUPPORTED_ADDRESS_INTENTS.has(resolverIntent)) {
return {
intent: resolverIntent,
confidence: toNonEmptyString(resolved?.confidence, deps) ?? "medium",
reason: "address_intent_resolver_current_turn_signal"
};
}
if (/(?:\u043a\u0442\u043e\s+\u043d\u0430\u043c\s+\u0434\u043e\u043b\u0436|\u043d\u0430\u043c\s+\u043a\u0442\u043e\s+\u0434\u043e\u043b\u0436|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u0434\u0435\u0431\u0438\u0442\u043e\u0440\u0441\u043a|\breceivables?\b)/iu.test(text)) {
return {
intent: "receivables_confirmed_as_of_date",
confidence: "high",
reason: "receivables_current_turn_meaning_signal"
};
}
if (/(?:\u043a\u043e\u043c\u0443\s+\u043c\u044b\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u043a\u043e\u043c\u0443\s+\u0434\u043e\u043b\u0436|\u043c\u044b\s+\u0434\u043e\u043b\u0436\u043d|\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440|\bpayables?\b)/iu.test(text)) {
return {
intent: "payables_confirmed_as_of_date",
confidence: "high",
reason: "payables_current_turn_meaning_signal"
};
}
if (/(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0434\u043e\u043a\u0438|docs?|documents?)/iu.test(text) && /(?:\u043f\u043e|by)\s+[\p{L}0-9._-]{2,}/iu.test(text)) {
return {
intent: "list_documents_by_counterparty",
confidence: "medium",
reason: "counterparty_documents_current_turn_signal"
};
}
if (/(?:\u043e\u0441\u0442\u0430\u0442|\u0441\u043a\u043b\u0430\u0434|inventory|stock)/iu.test(text)) {
return {
intent: "inventory_on_hand_as_of_date",
confidence: "medium",
reason: "inventory_snapshot_current_turn_signal"
};
}
return null;
}
function detectCounterpartyTurnoverFamily(text) {
const hasTurnoverCue = /(?:\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a|\u0434\u043e\u0445\u043e\u0434|turnover|revenue)/iu.test(text);
if (!hasTurnoverCue) {
return null;
}
const explicitEntityMatch = text.match(/(?:\u043f\u043e|by|for)?\s*([\p{L}0-9._-]{2,})\s*$/iu);
const rawEntity = explicitEntityMatch?.[1] ?? null;
const ignored = new Set([
"\u043e\u0431\u043e\u0440\u043e\u0442",
"\u0432\u044b\u0440\u0443\u0447\u043a\u0430",
"\u0434\u043e\u0445\u043e\u0434",
"\u0431\u044b\u043b",
"\u0431\u044b\u043b\u0430",
"turnover",
"revenue"
]);
const entity = rawEntity && !ignored.has(rawEntity) ? rawEntity : null;
return {
family: "counterparty_value_or_turnover",
entity
};
}
function buildEntityCandidates(counterpartyTurnover) {
if (!counterpartyTurnover?.entity) {
return [];
}
return [
{
type: "counterparty",
value: counterpartyTurnover.entity,
source: "current_turn_loose_entity_tail"
}
];
}
export function createAssistantTurnMeaningPolicy(deps = {}) {
function resolveAssistantTurnMeaning(input = {}) {
const rawMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
const effectiveMessage = String(input?.effectiveAddressUserMessage ?? rawMessage);
const rawText = normalizeTurnText(rawMessage, deps);
const effectiveText = normalizeTurnText(effectiveMessage, deps);
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
const supportedIntent = detectSupportedIntent(joinedText, deps);
const counterpartyTurnover = detectCounterpartyTurnoverFamily(joinedText);
const llmIntent = toNonEmptyString(input?.llmPreDecomposeMeta?.predecomposeContract?.intent, deps);
const explicitIntentCandidate =
supportedIntent?.intent ?? (llmIntent && llmIntent !== "unknown" ? llmIntent : null);
const unsupportedFamily = !explicitIntentCandidate && counterpartyTurnover?.family ? counterpartyTurnover.family : null;
const reasonCodes = [];
if (supportedIntent?.reason) {
reasonCodes.push(supportedIntent.reason);
}
if (counterpartyTurnover?.family) {
reasonCodes.push("counterparty_turnover_current_turn_signal");
}
if (rawText !== normalizeTurnText(rawMessage, { ...deps, repairAddressMojibake: (value) => String(value ?? "") })) {
reasonCodes.push("mojibake_repair_applied");
}
if (
rawText.includes("\u043d\u0430\u043c") &&
/(^|[^\p{L}0-9_])\u043d\u0430\u043c\u0441(?=$|[^\p{L}0-9_])/iu.test(String(rawMessage ?? ""))
) {
reasonCodes.push("known_turn_typo_normalized");
}
const askedDomainFamily =
explicitIntentCandidate?.startsWith("receivables_")
? "receivables"
: explicitIntentCandidate?.startsWith("payables_")
? "payables"
: explicitIntentCandidate?.startsWith("inventory_")
? "inventory"
: explicitIntentCandidate?.includes("counterparty")
? "counterparty"
: counterpartyTurnover?.family
? "counterparty"
: null;
const askedActionFamily =
explicitIntentCandidate === "receivables_confirmed_as_of_date" ||
explicitIntentCandidate === "payables_confirmed_as_of_date" ||
explicitIntentCandidate === "inventory_on_hand_as_of_date"
? "confirmed_snapshot"
: explicitIntentCandidate === "list_documents_by_counterparty"
? "list_documents"
: counterpartyTurnover?.family
? "counterparty_value_or_turnover"
: null;
const staleReplayForbidden = Boolean(unsupportedFamily || (counterpartyTurnover?.entity && !explicitIntentCandidate));
return {
schema_version: "assistant_turn_meaning_v1",
raw_message: rawMessage,
effective_message: effectiveMessage,
normalized_raw_message: rawText,
normalized_effective_message: effectiveText,
asked_domain_family: askedDomainFamily,
asked_action_family: askedActionFamily,
explicit_intent_candidate: explicitIntentCandidate,
explicit_entity_candidates: buildEntityCandidates(counterpartyTurnover),
meaning_confidence: supportedIntent?.confidence ?? (counterpartyTurnover?.family ? "medium" : "low"),
intent_override_strength: explicitIntentCandidate
? "explicit_current_turn_intent"
: staleReplayForbidden
? "explicit_new_action_or_entity"
: "none",
carryover_budget: staleReplayForbidden ? "none" : explicitIntentCandidate ? "matching_family_only" : "normal",
unsupported_but_understood_family: unsupportedFamily,
stale_replay_forbidden: staleReplayForbidden,
reason_codes: reasonCodes
};
}
return {
resolveAssistantTurnMeaning
};
}

View File

@ -14,6 +14,15 @@ describe("addressIntentResolver regression bridges", () => {
expect(result.intent).toBe("payables_confirmed_as_of_date"); expect(result.intent).toBe("payables_confirmed_as_of_date");
}); });
it("detects receivables snapshot wording through light current-turn typo noise", () => {
const result = resolveAddressIntent(
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f"
);
expect(result.intent).toBe("receivables_confirmed_as_of_date");
expect(result.reasons).toContain("current_turn_noise_normalized");
});
it("detects top customer all-time revenue wording", () => { it("detects top customer all-time revenue wording", () => {
const result = resolveAddressIntent("кто у нас самый доходный клиент за все время"); const result = resolveAddressIntent("кто у нас самый доходный клиент за все время");

View File

@ -670,4 +670,34 @@ describe("assistantRoutePolicy", () => {
expect(decision.toolGateDecision).toBe("run_address_lane"); expect(decision.toolGateDecision).toBe("run_address_lane");
expect(decision.livingMode).toBe("address_data"); expect(decision.livingMode).toBe("address_data");
}); });
it("recovers an address route from current-turn meaning when L0 resolver is noisy", () => {
const policy = buildPolicy({
resolveAddressToolGateDecision: undefined,
resolveAssistantTurnMeaning: () => ({
schema_version: "assistant_turn_meaning_v1",
explicit_intent_candidate: "receivables_confirmed_as_of_date",
meaning_confidence: "high",
reason_codes: ["receivables_current_turn_meaning_signal"],
stale_replay_forbidden: false
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f",
effectiveAddressUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.address_intent).toBe("receivables_confirmed_as_of_date");
expect(decision.orchestrationContract?.assistant_turn_meaning?.schema_version).toBe(
"assistant_turn_meaning_v1"
);
});
}); });

View File

@ -921,4 +921,48 @@ describe("assistantTransitionPolicy", () => {
}); });
expect(carryover?.followupContext?.previous_filters?.as_of_date).not.toBe("2021-04-15"); expect(carryover?.followupContext?.previous_filters?.as_of_date).not.toBe("2021-04-15");
}); });
it("drops carryover when current-turn meaning forbids stale replay", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: "Documents by previous counterparty",
debug: {
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
detected_intent: "list_documents_by_counterparty",
extracted_filters: {
counterparty: "Previous Counterparty",
organization: "Org Alt"
},
anchor_type: "counterparty",
anchor_value_resolved: "Previous Counterparty"
}
}),
hasAddressFollowupContextSignal: () => true,
resolveAssistantTurnMeaning: () => ({
schema_version: "assistant_turn_meaning_v1",
asked_domain_family: "counterparty",
asked_action_family: "counterparty_value_or_turnover",
unsupported_but_understood_family: "counterparty_value_or_turnover",
explicit_entity_candidates: [
{
type: "counterparty",
value: "svk",
source: "current_turn_loose_entity_tail"
}
],
stale_replay_forbidden: true
})
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a",
[],
null,
null,
null
);
expect(carryover).toBeNull();
});
}); });

View File

@ -0,0 +1,57 @@
import { describe, expect, it } from "vitest";
import { createAssistantTurnMeaningPolicy } from "../src/services/assistantTurnMeaningPolicy";
import { resolveAddressIntent } from "../src/services/addressIntentResolver";
function buildPolicy() {
return createAssistantTurnMeaningPolicy({
compactWhitespace: (value: string) => String(value ?? "").replace(/\s+/g, " ").trim(),
repairAddressMojibake: (value: string) => value,
resolveAddressIntent,
toNonEmptyString: (value: unknown) => {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
});
}
describe("assistantTurnMeaningPolicy", () => {
it("recovers a supported receivables intent from light current-turn typo noise", () => {
const policy = buildPolicy();
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f"
});
expect(meaning.schema_version).toBe("assistant_turn_meaning_v1");
expect(meaning.explicit_intent_candidate).toBe("receivables_confirmed_as_of_date");
expect(meaning.asked_domain_family).toBe("receivables");
expect(meaning.carryover_budget).toBe("matching_family_only");
expect(meaning.stale_replay_forbidden).toBe(false);
});
it("marks unsupported counterparty turnover as understood and forbids stale replay", () => {
const policy = buildPolicy();
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a"
});
expect(meaning.explicit_intent_candidate).toBeNull();
expect(meaning.asked_domain_family).toBe("counterparty");
expect(meaning.asked_action_family).toBe("counterparty_value_or_turnover");
expect(meaning.unsupported_but_understood_family).toBe("counterparty_value_or_turnover");
expect(meaning.stale_replay_forbidden).toBe(true);
expect(meaning.explicit_entity_candidates).toEqual([
{
type: "counterparty",
value: "\u0441\u0432\u043a",
source: "current_turn_loose_entity_tail"
}
]);
});
});