NODEDC_1C/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts

1118 lines
50 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-nocheck
import {
buildInventoryRootFrameFromAddressDebug,
readAddressDebugFilters,
readAddressDebugItem,
resolveAssistantContinuitySnapshot
} from "./assistantContinuityPolicy";
export function createAssistantTransitionPolicy(deps) {
function normalizeFollowupText(value) {
return deps.compactWhitespace(deps.repairAddressMojibake(String(value ?? "")).toLowerCase()).replace(/ё/g, "е");
}
function hasSelectedObjectInventoryScopeSignal(text) {
const normalized = normalizeFollowupText(text);
if (!normalized) {
return false;
}
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(
normalized
);
}
function extractSelectedObjectLabel(text) {
const repaired = deps.repairAddressMojibake(String(text ?? ""));
const quotedMatch = repaired.match(
/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)\s*[:,-]?\s*[«"]([^"»]+?)["»]/iu
);
if (quotedMatch?.[1]) {
return deps.toNonEmptyString(quotedMatch[1]);
}
return null;
}
function inferSelectedObjectInventoryFollowupIntent(userMessage, alternateMessage = null) {
const samples = [userMessage, alternateMessage].map((item) => normalizeFollowupText(item)).filter(Boolean);
if (samples.length === 0) {
return null;
}
if (
samples.some((sample) =>
/(?:где\s+(?:мы\s+)?взял|откуда\s+(?:мы\s+)?взял|у\s+кого\s+купил|кто\s+постав|поставщик|источник\s+поступл|от\s+кого\s+поступ)/iu.test(
sample
)
)
) {
return "inventory_purchase_provenance_for_item";
}
if (
samples.some((sample) =>
/(?:документ|накладн|счет|сч[её]т|акт|поступл|покажи\s+док|все\s+документ|все\s+операц)/iu.test(sample)
)
) {
return "inventory_purchase_documents_for_item";
}
if (
samples.some((sample) =>
/(?:кому\s+продал|кому\s+ушл|куда\s+продал|кто\s+купил|реализац|отгруз|продаж)/iu.test(sample)
)
) {
return "inventory_sale_trace_for_item";
}
if (samples.some((sample) => /(?:рентабел|маржин|прибыл|наценк|выгод)/iu.test(sample))) {
return "inventory_profitability_for_item";
}
if (samples.some((sample) => /(?:цепочк|от\s+закупк.*до\s+продаж|от\s+покупк.*до\s+продаж)/iu.test(sample))) {
return "inventory_purchase_to_sale_chain";
}
return null;
}
function parseDmyDateToIso(value) {
const match = String(value ?? "").trim().match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
if (!match) {
return null;
}
return `${match[3]}-${match[2]}-${match[1]}`;
}
function computeMonthWindowFromIso(isoDate) {
const match = String(isoDate ?? "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
if (!Number.isFinite(year) || !Number.isFinite(month) || month < 1 || month > 12) {
return null;
}
const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate();
const mm = String(month).padStart(2, "0");
const dd = String(lastDay).padStart(2, "0");
return {
purchase_date: isoDate,
period_from: `${year}-${mm}-01`,
period_to: `${year}-${mm}-${dd}`,
as_of_date: `${year}-${mm}-${dd}`
};
}
function extractEarliestDmyDateFromEntityRefs(entityRefs) {
const dates = [];
for (const entityRef of Array.isArray(entityRefs) ? entityRefs : []) {
const value = deps.toNonEmptyString(entityRef?.value);
if (!value) {
continue;
}
const matches = String(value).match(/\b(\d{2}\.\d{2}\.\d{4})\b/g);
if (!matches) {
continue;
}
for (const token of matches) {
const isoDate = parseDmyDateToIso(token);
if (isoDate) {
dates.push(isoDate);
}
}
}
if (dates.length === 0) {
return null;
}
return dates.sort()[0] ?? null;
}
function extractPurchaseDateBridgeWindow(previousAddressItem, addressNavigationState) {
const replyText = deps.toNonEmptyString(previousAddressItem?.text);
if (replyText) {
const repairedText = deps.repairAddressMojibake(replyText);
const explicitFirstDateMatch = repairedText.match(/первая\s+найденная\s+дата\s+закупки:\s*(\d{2}\.\d{2}\.\d{4})/iu);
const explicitFirstDateIso = explicitFirstDateMatch ? parseDmyDateToIso(explicitFirstDateMatch[1]) : null;
if (explicitFirstDateIso) {
return computeMonthWindowFromIso(explicitFirstDateIso);
}
}
const sessionContext =
addressNavigationState &&
typeof addressNavigationState === "object" &&
addressNavigationState.session_context &&
typeof addressNavigationState.session_context === "object"
? addressNavigationState.session_context
: null;
const focusObject =
sessionContext && typeof sessionContext.active_focus_object === "object"
? sessionContext.active_focus_object
: null;
const preferredResultSetId =
deps.toNonEmptyString(focusObject?.provenance_result_set_id) ??
deps.toNonEmptyString(sessionContext?.active_result_set_id);
const resultSets = Array.isArray(addressNavigationState?.result_sets) ? addressNavigationState.result_sets : [];
const preferredResultSet =
(preferredResultSetId
? resultSets.find((item) => deps.toNonEmptyString(item?.result_set_id) === preferredResultSetId) ?? null
: null) ??
resultSets.find((item) => deps.toNonEmptyString(item?.intent) === "inventory_purchase_provenance_for_item") ??
null;
const earliestIsoDate = extractEarliestDmyDateFromEntityRefs(preferredResultSet?.entity_refs);
return earliestIsoDate ? computeMonthWindowFromIso(earliestIsoDate) : null;
}
function isUsableFollowupSourceDebug(debug) {
if (!debug || typeof debug !== "object") {
return false;
}
const executionLane = deps.toNonEmptyString(debug.execution_lane);
const detectedIntent = deps.toNonEmptyString(debug.detected_intent);
const selectedRecipe = deps.toNonEmptyString(debug.selected_recipe);
const answerGroundingCheck =
debug.answer_grounding_check && typeof debug.answer_grounding_check === "object"
? debug.answer_grounding_check
: null;
const groundingStatus = deps.toNonEmptyString(answerGroundingCheck?.status);
if (groundingStatus === "grounded") {
return true;
}
if (selectedRecipe) {
return true;
}
return executionLane === "address_query" && Boolean(detectedIntent && detectedIntent !== "unknown");
}
function findRecentUsableAddressAssistantItem(items) {
for (let index = Array.isArray(items) ? items.length - 1 : -1; index >= 0; index -= 1) {
const item = items[index];
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
continue;
}
if (isUsableFollowupSourceDebug(item.debug)) {
return item;
}
}
return null;
}
function findRecentInventoryPurchaseProvenanceItem(items, itemHint = null) {
const normalizedItemHint = deps.toNonEmptyString(itemHint);
for (let index = Array.isArray(items) ? items.length - 1 : -1; index >= 0; index -= 1) {
const item = items[index];
const debug = item?.debug;
if (!item || item.role !== "assistant" || !debug || typeof debug !== "object") {
continue;
}
if (deps.toNonEmptyString(debug.detected_intent) !== "inventory_purchase_provenance_for_item") {
continue;
}
if (!normalizedItemHint) {
return item;
}
const candidateItem = readAddressDebugItem(debug, deps.toNonEmptyString);
if (candidateItem && candidateItem === normalizedItemHint) {
return item;
}
}
return null;
}
function hasInventoryPurchaseDateVatBridgeSignal(userMessage, alternateMessage, sourceIntentHint, hasInventoryItemFocusHint) {
if (
sourceIntentHint !== "inventory_purchase_provenance_for_item" &&
!hasInventoryItemFocusHint &&
!deps.isInventorySelectedObjectIntent(sourceIntentHint)
) {
return false;
}
const samples = [userMessage, alternateMessage]
.map((item) => deps.compactWhitespace(deps.repairAddressMojibake(String(item ?? "")).toLowerCase()))
.filter((item) => item.length > 0);
if (samples.length === 0) {
return false;
}
return samples.some(
(sample) =>
/(?:ндс|vat)/iu.test(sample) &&
/(?:на\s+дат[ауеы]\s+покупк|на\s+дат[ауеы]\s+закупк|по\s+дат[еу]\s+покупк|по\s+дат[еу]\s+закупк|дата\s+покупк|дата\s+закупк|purchase\s+date)/iu.test(
sample
)
);
}
function hasInventoryRootRestatementLikeSignal(userMessage, sourceIntentHint, hasInventoryRootFrame) {
if (!hasInventoryRootFrame) {
return false;
}
const normalized = deps
.compactWhitespace(deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase())
.replace(/ё/g, "е");
if (!normalized) {
return false;
}
if (deps.countTokens(normalized) > 10) {
return false;
}
const hasInventoryLexeme = /(?:остат|склад|товар|номенклатур|позиц)/iu.test(normalized);
const hasRestatementCue =
/(?:еще\s+раз|снова|повтори|повтори\s+еще\s+раз|верни|покажи)/iu.test(normalized) &&
/(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|на\s+эту\s+дат[ауеы]|эту\s+дат[ауеы]|та\s+же\s+дата|тот\s+же\s+период|этот\s+же\s+период)/iu.test(
normalized
);
const hasBareSnapshotSameDateCue =
hasInventoryLexeme &&
/(?:РЅР°\s+ССѓ\s+РРµ\s+РґР°С[ауеС]|РЅР°\s+СЌССѓ\s+РРµ\s+РґР°С[ауеС]|РЅР°\s+СЌССѓ\s+РґР°С[ауеС]|СЌССѓ\s+РґР°С[ауеС]|СР°\s+РРµ\s+РґР°СР°|СРѕС\s+РРµ\s+период|СЌСРѕС\s+РРµ\s+период)/iu.test(
normalized
);
const bareSnapshotSameDatePhrases = [
"\u043d\u0430 \u0442\u0443 \u0436\u0435 \u0434\u0430\u0442",
"\u043d\u0430 \u044d\u0442\u0443 \u0436\u0435 \u0434\u0430\u0442",
"\u043d\u0430 \u044d\u0442\u0443 \u0434\u0430\u0442",
"\u044d\u0442\u0443 \u0434\u0430\u0442",
"\u0442\u0430 \u0436\u0435 \u0434\u0430\u0442\u0430",
"\u0442\u043e\u0442 \u0436\u0435 \u043f\u0435\u0440\u0438\u043e\u0434",
"\u044d\u0442\u043e\u0442 \u0436\u0435 \u043f\u0435\u0440\u0438\u043e\u0434"
];
const hasBareSnapshotSameDatePhraseCue =
hasInventoryLexeme && bareSnapshotSameDatePhrases.some((phrase) => normalized.includes(phrase));
return (
hasInventoryLexeme &&
(hasRestatementCue || hasBareSnapshotSameDateCue || hasBareSnapshotSameDatePhraseCue) &&
!deps.hasForeignAccountingPivotOverInventoryMessage(normalized)
);
}
function hasExplicitInventorySameDatePivotSignal(userMessage) {
const normalized = deps
.compactWhitespace(deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase())
.replace(/ё/g, "е");
if (!normalized) {
return false;
}
const hasInventoryLexeme = /(?:остат|склад|товар|номенклатур|позиц)/iu.test(normalized);
if (!hasInventoryLexeme) {
return false;
}
const sameDatePhrases = [
"на ту же дат",
"на эту же дат",
"на эту дат",
"эту дат",
"та же дата",
"тот же период",
"этот же период"
];
return sameDatePhrases.some((phrase) => normalized.includes(phrase));
}
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
const normalized = deps.compactWhitespace(
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
);
if (!normalized || deps.countTokens(normalized) > 4) {
return false;
}
if (sourceIntent !== "list_documents_by_counterparty" && sourceIntent !== "list_documents_by_contract") {
return false;
}
if (
/(?:банк|РІСРїРёСЃРє|плаС[РµС]Р|оплаС|списан|РїРѕСЃСуплен|bank|payment|wire|statement)/iu.test(
normalized
)
) {
return false;
}
return /^(?:Р°|Рё|РЅСѓ)?\s*РїРѕ\s+[a-zР°-СЏС0-9._-]{2,}(?:\s+[a-zР°-СЏС0-9._-]{2,})?$/iu.test(normalized);
}
function shouldKeepPreviousIntentForShortCounterpartyRetargetV2(userMessage, sourceIntent) {
const normalized = deps.compactWhitespace(
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
);
if (!normalized || deps.countTokens(normalized) > 4) {
return false;
}
if (sourceIntent !== "list_documents_by_counterparty" && sourceIntent !== "list_documents_by_contract") {
return false;
}
if (/(?:bank|payment|wire|statement)/iu.test(normalized)) {
return false;
}
return /^(?:а|и|ну)?\s*(?:покажи\s+)?по\s+[a-zа-яё0-9._-]{2,}(?:\s+[a-zа-яё0-9._-]{2,})?$/iu.test(normalized);
}
function inferStandaloneAddressTopicFamily(userMessage) {
const normalized = deps.compactWhitespace(
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
);
if (!normalized) {
return null;
}
if (/(?:ндс|vat)/iu.test(normalized)) {
return "vat";
}
if (/(?:остат|склад|товар|номенклатур|позици)/iu.test(normalized)) {
return "inventory";
}
return null;
}
function resolveDisplayedEntityRetargetIntent(userMessage, entityType) {
const normalized = deps.compactWhitespace(
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
);
if (!normalized) {
return null;
}
if (entityType === "counterparty") {
if (/(?:договор|контракт)/iu.test(normalized)) {
return "list_contracts_by_counterparty";
}
if (/(?:банк|выписк|плат[её]ж|оплат|statement|payment|wire)/iu.test(normalized)) {
return "bank_operations_by_counterparty";
}
if (/(?:документ|накладн|счет|сч[её]т|акт|реализац|поступл)/iu.test(normalized)) {
return "list_documents_by_counterparty";
}
if (/(?:сколько\s+денег|сколько\s+принес|выручк|сумм[аы]?|оплатил|продаж)/iu.test(normalized)) {
return "customer_revenue_and_payments";
}
return null;
}
if (entityType === "contract") {
if (/(?:банк|выписк|плат[её]ж|оплат|statement|payment|wire)/iu.test(normalized)) {
return "bank_operations_by_contract";
}
if (/(?:документ|накладн|счет|сч[её]т|акт|реализац|поступл)/iu.test(normalized)) {
return "list_documents_by_contract";
}
}
return null;
}
function resolveAddressFollowupCarryoverContext(
userMessage,
items,
alternateMessage = null,
llmPreDecomposeMeta = null,
addressNavigationState = null
) {
const rawCapabilityMetaQuery =
deps.shouldHandleAsAssistantCapabilityMetaQuery(userMessage) ||
(deps.toNonEmptyString(alternateMessage)
? deps.shouldHandleAsAssistantCapabilityMetaQuery(String(alternateMessage ?? ""))
: false);
const rawDataRetrievalSignal =
deps.hasDataRetrievalRequestSignal(userMessage) ||
(deps.toNonEmptyString(alternateMessage)
? deps.hasDataRetrievalRequestSignal(String(alternateMessage ?? ""))
: false);
if (rawCapabilityMetaQuery && !rawDataRetrievalSignal) {
return null;
}
const latestAddressItem = deps.findLastAddressAssistantItem(items);
const previousAddressItem =
(latestAddressItem && isUsableFollowupSourceDebug(latestAddressItem?.debug)
? latestAddressItem
: findRecentUsableAddressAssistantItem(items)) ?? latestAddressItem;
const previousAddressDebug = previousAddressItem?.debug ?? null;
const continuitySnapshot = resolveAssistantContinuitySnapshot({
sessionItems: items,
toNonEmptyString: deps.toNonEmptyString
});
const lastOrganizationClarificationDebug = deps.findLastOrganizationClarificationAddressDebug(items);
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
? deps.mergeKnownOrganizations(lastOrganizationClarificationDebug.organization_candidates)
: [];
const organizationClarificationSelection =
deps.resolveOrganizationSelectionFromMessage(userMessage, organizationClarificationCandidates) ??
(deps.toNonEmptyString(alternateMessage)
? deps.resolveOrganizationSelectionFromMessage(String(alternateMessage ?? ""), organizationClarificationCandidates)
: null);
const hasOrganizationClarificationContinuation = Boolean(
lastOrganizationClarificationDebug && organizationClarificationSelection
);
const followupOffer = previousAddressDebug ? deps.buildAddressFollowupOffer(previousAddressDebug) : null;
const hasImplicitContinuationSignal =
Boolean(previousAddressDebug) &&
Boolean(followupOffer?.enabled) &&
(deps.isImplicitAddressContinuationByLlm(userMessage, llmPreDecomposeMeta) ||
(deps.toNonEmptyString(alternateMessage)
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
: false));
const sourceIntentHint = deps.toNonEmptyString(previousAddressDebug?.detected_intent);
const navigationFocusObjectHint =
addressNavigationState &&
typeof addressNavigationState === "object" &&
addressNavigationState.session_context &&
typeof addressNavigationState.session_context === "object" &&
addressNavigationState.session_context.active_focus_object &&
typeof addressNavigationState.session_context.active_focus_object === "object"
? addressNavigationState.session_context.active_focus_object
: null;
const hasNavigationInventoryItemFocusHint = Boolean(
deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" &&
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
deps.isInventorySelectedObjectIntent(sourceIntentHint))
);
const inventoryPurchaseDateVatBridge = hasInventoryPurchaseDateVatBridgeSignal(
userMessage,
alternateMessage,
sourceIntentHint,
hasNavigationInventoryItemFocusHint
);
let inventoryShortFollowupPrimary =
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
deps.hasShortInventoryObjectFollowupSignal(userMessage);
let inventoryShortFollowupAlternate =
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
deps.toNonEmptyString(alternateMessage)
? deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""))
: false;
const debtRoleSwapPrimary = sourceIntentHint
? deps.resolveDebtRoleSwapFollowupIntent(userMessage, sourceIntentHint)
: null;
const debtRoleSwapAlternate =
sourceIntentHint && deps.toNonEmptyString(alternateMessage)
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
: null;
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
let hasPrimaryFollowupSignal =
deps.hasAddressFollowupContextSignal(userMessage) ||
Boolean(debtRoleSwapPrimary) ||
inventoryShortFollowupPrimary ||
inventoryPurchaseDateVatBridge;
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
Boolean(debtRoleSwapAlternate) ||
inventoryShortFollowupAlternate ||
inventoryPurchaseDateVatBridge
: false;
const hasPrimaryIndexReferenceSignal = deps.extractDisplayedEntityIndexMention(userMessage) !== null;
const hasAlternateIndexReferenceSignal = deps.toNonEmptyString(alternateMessage)
? deps.extractDisplayedEntityIndexMention(String(alternateMessage ?? "")) !== null
: false;
const hasIndexReferenceSignal = hasPrimaryIndexReferenceSignal || hasAlternateIndexReferenceSignal;
const recentInventoryRootFrame = deps.findRecentInventoryRootFrame(items);
const hasInventoryRootTemporalFollowupPrimary = deps.hasInventoryRootTemporalFollowupSignal(
userMessage,
sourceIntentHint,
Boolean(recentInventoryRootFrame)
);
const hasInventoryRootTemporalFollowupAlternate = deps.toNonEmptyString(alternateMessage)
? deps.hasInventoryRootTemporalFollowupSignal(
String(alternateMessage ?? ""),
sourceIntentHint,
Boolean(recentInventoryRootFrame)
)
: false;
const hasInventoryRootRestatementPrimary = hasInventoryRootRestatementLikeSignal(
userMessage,
sourceIntentHint,
Boolean(recentInventoryRootFrame)
);
const hasInventoryRootRestatementAlternate = deps.toNonEmptyString(alternateMessage)
? hasInventoryRootRestatementLikeSignal(
String(alternateMessage ?? ""),
sourceIntentHint,
Boolean(recentInventoryRootFrame)
)
: false;
const hasExplicitInventorySameDatePivotPrimary = hasExplicitInventorySameDatePivotSignal(userMessage);
const hasExplicitInventorySameDatePivotAlternate = deps.toNonEmptyString(alternateMessage)
? hasExplicitInventorySameDatePivotSignal(String(alternateMessage ?? ""))
: false;
let hasStrongFollowupReference =
hasPrimaryIndexReferenceSignal ||
hasAlternateIndexReferenceSignal ||
hasOrganizationClarificationContinuation ||
hasImplicitContinuationSignal ||
inventoryShortFollowupPrimary ||
inventoryShortFollowupAlternate ||
hasInventoryRootTemporalFollowupPrimary ||
hasInventoryRootTemporalFollowupAlternate ||
hasInventoryRootRestatementPrimary ||
hasInventoryRootRestatementAlternate ||
inventoryPurchaseDateVatBridge ||
Boolean(debtRoleSwapIntent) ||
deps.hasFollowupMarker(userMessage) ||
deps.hasReferentialPointer(userMessage) ||
(deps.toNonEmptyString(alternateMessage)
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
: false);
const hasStandaloneAddressTopic =
deps.hasStandaloneAddressTopicSignal(userMessage) ||
(deps.toNonEmptyString(alternateMessage) ? deps.hasStandaloneAddressTopicSignal(alternateMessage) : false);
if (
hasStandaloneAddressTopic &&
!hasPrimaryFollowupSignal &&
!hasAlternateFollowupSignal &&
!hasInventoryRootTemporalFollowupPrimary &&
!hasInventoryRootTemporalFollowupAlternate &&
!hasInventoryRootRestatementPrimary &&
!hasInventoryRootRestatementAlternate &&
!hasImplicitContinuationSignal &&
!hasOrganizationClarificationContinuation &&
!hasIndexReferenceSignal
) {
return null;
}
if (
!hasPrimaryFollowupSignal &&
!hasAlternateFollowupSignal &&
!hasInventoryRootTemporalFollowupPrimary &&
!hasInventoryRootTemporalFollowupAlternate &&
!hasInventoryRootRestatementPrimary &&
!hasInventoryRootRestatementAlternate &&
!hasImplicitContinuationSignal &&
!hasOrganizationClarificationContinuation &&
!hasIndexReferenceSignal
) {
return null;
}
if (!previousAddressDebug) {
return null;
}
const sourceIntent = deps.toNonEmptyString(previousAddressDebug.detected_intent);
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const llmSelectedObjectScopeDetected =
llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true;
const resolvedPrimaryIntent = deps.resolveAddressIntent(deps.repairAddressMojibake(String(userMessage ?? ""))).intent;
const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage)
? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent
: null;
const explicitIntent =
llmExplicitIntent && llmExplicitIntent !== "unknown"
? llmExplicitIntent
: resolvedPrimaryIntent && resolvedPrimaryIntent !== "unknown"
? resolvedPrimaryIntent
: resolvedAlternateIntent && resolvedAlternateIntent !== "unknown"
? resolvedAlternateIntent
: null;
const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent);
const explicitIntentFamily =
deps.resolveAddressIntentFamily(explicitIntent) ??
inferStandaloneAddressTopicFamily(userMessage) ??
(deps.toNonEmptyString(alternateMessage) ? inferStandaloneAddressTopicFamily(String(alternateMessage ?? "")) : null);
const foreignAccountingPivotOverInventory = deps.hasForeignAccountingPivotOverInventoryMessage(
userMessage,
alternateMessage
);
const familyMismatchStandaloneTopic = Boolean(
hasStandaloneAddressTopic &&
sourceIntentFamily &&
explicitIntentFamily &&
sourceIntentFamily !== explicitIntentFamily &&
!hasOrganizationClarificationContinuation &&
!hasImplicitContinuationSignal &&
!hasIndexReferenceSignal &&
!hasInventoryRootTemporalFollowupPrimary &&
!hasInventoryRootTemporalFollowupAlternate &&
!hasInventoryRootRestatementPrimary &&
!hasInventoryRootRestatementAlternate &&
!inventoryShortFollowupPrimary &&
!inventoryShortFollowupAlternate &&
!foreignAccountingPivotOverInventory &&
!deps.hasFollowupMarker(userMessage) &&
!deps.hasReferentialPointer(userMessage) &&
(!deps.toNonEmptyString(alternateMessage)
? true
: !deps.hasFollowupMarker(String(alternateMessage ?? "")) &&
!deps.hasReferentialPointer(String(alternateMessage ?? "")))
);
if (familyMismatchStandaloneTopic) {
return null;
}
if (
sourceIntentFamily &&
explicitIntentFamily &&
sourceIntentFamily !== explicitIntentFamily &&
!hasStrongFollowupReference &&
!foreignAccountingPivotOverInventory
) {
return null;
}
let previousIntent = sourceIntent;
let followupSelectionMode = "carry_previous_intent";
if (debtRoleSwapIntent) {
previousIntent = debtRoleSwapIntent;
}
if (hasImplicitContinuationSignal) {
const suggestedIntent = Array.isArray(followupOffer?.suggested_intents)
? deps.toNonEmptyString(followupOffer.suggested_intents[0])
: null;
const keepPreviousIntent = shouldKeepPreviousIntentForShortCounterpartyRetargetV2(userMessage, sourceIntent);
if (suggestedIntent && !keepPreviousIntent) {
previousIntent = suggestedIntent;
followupSelectionMode = "switch_to_suggested_intent";
}
}
let previousAnchorType = deps.toNonEmptyString(previousAddressDebug.anchor_type);
let previousAnchor =
deps.toNonEmptyString(previousAddressDebug.anchor_value_resolved) ??
deps.toNonEmptyString(previousAddressDebug.anchor_value_raw) ??
deps.readAddressFilterString(previousAddressDebug, "item") ??
deps.readAddressFilterString(previousAddressDebug, "counterparty") ??
deps.readAddressFilterString(previousAddressDebug, "account") ??
deps.readAddressFilterString(previousAddressDebug, "contract");
const navigationSessionContext =
addressNavigationState && typeof addressNavigationState === "object"
? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object"
? addressNavigationState.session_context
: null
: null;
const navigationDateScope =
navigationSessionContext && typeof navigationSessionContext.date_scope === "object"
? navigationSessionContext.date_scope
: null;
const navigationOrganization = deps.normalizeOrganizationScopeValue(navigationSessionContext?.organization_scope);
const navigationFocusObject =
navigationSessionContext && typeof navigationSessionContext.active_focus_object === "object"
? navigationSessionContext.active_focus_object
: null;
const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.object_type);
const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
const hasInventoryItemFocusCarryover =
navigationFocusObjectType === "item" &&
navigationFocusObjectLabel &&
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
deps.isInventorySelectedObjectIntent(sourceIntentHint));
if (!inventoryShortFollowupPrimary && hasInventoryItemFocusCarryover) {
inventoryShortFollowupPrimary = deps.hasShortInventoryObjectFollowupSignal(userMessage);
}
if (!inventoryShortFollowupAlternate && hasInventoryItemFocusCarryover && deps.toNonEmptyString(alternateMessage)) {
inventoryShortFollowupAlternate = deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""));
}
hasPrimaryFollowupSignal =
deps.hasAddressFollowupContextSignal(userMessage) ||
Boolean(debtRoleSwapPrimary) ||
inventoryShortFollowupPrimary ||
inventoryPurchaseDateVatBridge ||
hasInventoryRootTemporalFollowupPrimary;
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
Boolean(debtRoleSwapAlternate) ||
inventoryShortFollowupAlternate ||
inventoryPurchaseDateVatBridge ||
hasInventoryRootTemporalFollowupAlternate
: false;
hasStrongFollowupReference =
hasPrimaryIndexReferenceSignal ||
hasAlternateIndexReferenceSignal ||
hasOrganizationClarificationContinuation ||
hasImplicitContinuationSignal ||
inventoryShortFollowupPrimary ||
inventoryShortFollowupAlternate ||
hasInventoryRootTemporalFollowupPrimary ||
hasInventoryRootTemporalFollowupAlternate ||
inventoryPurchaseDateVatBridge ||
Boolean(debtRoleSwapIntent) ||
deps.hasFollowupMarker(userMessage) ||
deps.hasReferentialPointer(userMessage) ||
(deps.toNonEmptyString(alternateMessage)
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
: false);
const hasSelectedObjectInventorySignalPrimary =
llmSelectedObjectScopeDetected || hasSelectedObjectInventoryScopeSignal(userMessage);
const hasSelectedObjectInventorySignalAlternate = deps.toNonEmptyString(alternateMessage)
? hasSelectedObjectInventoryScopeSignal(String(alternateMessage ?? ""))
: false;
const selectedObjectRetargetIntent =
hasSelectedObjectInventorySignalPrimary || hasSelectedObjectInventorySignalAlternate
? inferSelectedObjectInventoryFollowupIntent(userMessage, alternateMessage)
: null;
let inventoryRootFrame =
deps.findRecentInventoryRootFrame(items) ??
continuitySnapshot.inventoryRootFrame ??
buildInventoryRootFrameFromAddressDebug(continuitySnapshot.lastGroundedAddressDebug, deps.toNonEmptyString);
if (inventoryRootFrame && navigationOrganization && !deps.toNonEmptyString(inventoryRootFrame.filters?.organization)) {
inventoryRootFrame = {
...inventoryRootFrame,
filters: {
...(inventoryRootFrame.filters ?? {}),
organization: navigationOrganization
}
};
}
if (inventoryRootFrame && navigationDateScope) {
inventoryRootFrame = {
...inventoryRootFrame,
filters: {
...(inventoryRootFrame.filters ?? {}),
as_of_date:
deps.toNonEmptyString(inventoryRootFrame.filters?.as_of_date) ??
deps.toNonEmptyString(navigationDateScope.as_of_date) ??
undefined,
period_from:
deps.toNonEmptyString(inventoryRootFrame.filters?.period_from) ??
deps.toNonEmptyString(navigationDateScope.period_from) ??
undefined,
period_to:
deps.toNonEmptyString(inventoryRootFrame.filters?.period_to) ??
deps.toNonEmptyString(navigationDateScope.period_to) ??
undefined
}
};
}
let currentFrameKind = inventoryRootFrame
? deps.isInventoryDrilldownFrameIntent(sourceIntent)
? "inventory_drilldown"
: deps.isInventoryRootFrameIntent(sourceIntent)
? "inventory_root"
: "generic"
: null;
let resolvedCounterpartyFromDisplay = false;
let displayedEntityTargetIntent = null;
const previousFiltersRaw = previousAddressDebug.extracted_filters;
let previousFilters =
previousFiltersRaw && typeof previousFiltersRaw === "object" ? { ...previousFiltersRaw } : {};
const shouldBackfillHistoricalPartyAnchors =
sourceIntentHint === "list_contracts_by_counterparty" ||
sourceIntentHint === "list_documents_by_counterparty" ||
sourceIntentHint === "bank_operations_by_counterparty" ||
sourceIntentHint === "list_documents_by_contract" ||
sourceIntentHint === "bank_operations_by_contract" ||
sourceIntentHint === "open_items_by_counterparty_or_contract";
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.contract)) {
const historicalContract = deps.findRecentAddressFilterValue(items, "contract");
if (historicalContract) {
previousFilters.contract = historicalContract;
}
}
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.counterparty)) {
const historicalCounterparty = deps.findRecentAddressFilterValue(items, "counterparty");
if (historicalCounterparty) {
previousFilters.counterparty = historicalCounterparty;
}
}
if (!deps.toNonEmptyString(previousFilters.organization)) {
const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization");
if (historicalOrganization) {
previousFilters.organization = historicalOrganization;
}
}
if (!deps.toNonEmptyString(previousFilters.organization) && continuitySnapshot.activeOrganization) {
previousFilters.organization = continuitySnapshot.activeOrganization;
}
if (!deps.toNonEmptyString(previousFilters.organization) && navigationOrganization) {
previousFilters.organization = navigationOrganization;
}
if (!deps.toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) {
previousFilters.organization = organizationClarificationSelection;
}
if (inventoryPurchaseDateVatBridge) {
const purchaseBridgeItem =
previousAddressItem &&
deps.toNonEmptyString(previousAddressDebug?.detected_intent) === "inventory_purchase_provenance_for_item"
? previousAddressItem
: findRecentInventoryPurchaseProvenanceItem(
items,
deps.toNonEmptyString(navigationFocusObjectLabel) ??
readAddressDebugItem(previousAddressDebug, deps.toNonEmptyString) ??
deps.toNonEmptyString(previousFilters.item)
) ?? previousAddressItem;
const purchaseBridgeWindow = extractPurchaseDateBridgeWindow(purchaseBridgeItem, addressNavigationState);
if (purchaseBridgeWindow) {
previousFilters.period_from = purchaseBridgeWindow.period_from;
previousFilters.period_to = purchaseBridgeWindow.period_to;
}
}
const shouldBackfillPreviousDateScopeFromNavigation =
sourceIntentHint === "inventory_on_hand_as_of_date" ||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
sourceIntentHint === "inventory_sale_trace_for_item" ||
sourceIntentHint === "inventory_profitability_for_item" ||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
sourceIntentHint === "account_balance_snapshot" ||
sourceIntentHint === "documents_forming_balance";
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.as_of_date) &&
deps.toNonEmptyString(navigationDateScope?.as_of_date)
) {
previousFilters.as_of_date = deps.toNonEmptyString(navigationDateScope?.as_of_date);
}
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.as_of_date) &&
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.as_of_date
) {
previousFilters.as_of_date = deps.toNonEmptyString(
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.as_of_date
);
}
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.period_from) &&
deps.toNonEmptyString(navigationDateScope?.period_from)
) {
previousFilters.period_from = deps.toNonEmptyString(navigationDateScope?.period_from);
}
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.period_from) &&
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.period_from
) {
previousFilters.period_from = deps.toNonEmptyString(
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.period_from
);
}
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.period_to) &&
deps.toNonEmptyString(navigationDateScope?.period_to)
) {
previousFilters.period_to = deps.toNonEmptyString(navigationDateScope?.period_to);
}
if (
shouldBackfillPreviousDateScopeFromNavigation &&
!deps.toNonEmptyString(previousFilters.period_to) &&
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.period_to
) {
previousFilters.period_to = deps.toNonEmptyString(
readAddressDebugFilters(continuitySnapshot.lastGroundedAddressDebug)?.period_to
);
}
const rootContextOnlyPivot = Boolean(
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage) &&
!inventoryPurchaseDateVatBridge
);
const inventoryRootTemporalPivot = Boolean(
inventoryRootFrame &&
(deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
currentFrameKind === "inventory_drilldown" ||
currentFrameKind === "inventory_root") &&
(hasInventoryRootTemporalFollowupPrimary || hasInventoryRootTemporalFollowupAlternate) &&
!deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage)
);
const inventoryRootRestatementPivot = Boolean(
inventoryRootFrame &&
(deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
currentFrameKind === "inventory_drilldown" ||
currentFrameKind === "inventory_root" ||
currentFrameKind === "generic") &&
(hasInventoryRootRestatementPrimary || hasInventoryRootRestatementAlternate) &&
!deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage)
);
const explicitInventorySameDatePivot = Boolean(
!inventoryRootFrame &&
(hasExplicitInventorySameDatePivotPrimary || hasExplicitInventorySameDatePivotAlternate) &&
!deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage)
);
const rootScopedPivot = rootContextOnlyPivot || inventoryRootTemporalPivot || inventoryRootRestatementPivot;
if (rootScopedPivot) {
previousIntent = null;
previousAnchorType = null;
previousAnchor = null;
previousFilters = deps.buildRootScopedCarryoverFilters(previousFilters, inventoryRootFrame);
currentFrameKind = inventoryRootFrame ? "inventory_root" : currentFrameKind;
followupSelectionMode = "carry_root_context";
}
const displayedEntityType = deps.inferDisplayedEntityTypeFromIntent(sourceIntent);
const displayedEntities = deps.extractDisplayedAddressEntityCandidates(
deps.toNonEmptyString(previousAddressItem?.text) ?? "",
displayedEntityType
);
const resolvedEntityFromFollowup =
deps.resolveDisplayedAddressEntityMention(userMessage, displayedEntities) ??
(deps.toNonEmptyString(alternateMessage)
? deps.resolveDisplayedAddressEntityMention(String(alternateMessage ?? ""), displayedEntities)
: null);
if (resolvedEntityFromFollowup && !rootScopedPivot) {
displayedEntityTargetIntent = resolveDisplayedEntityRetargetIntent(
userMessage,
resolvedEntityFromFollowup.entityType
);
if (resolvedEntityFromFollowup.entityType === "counterparty") {
previousFilters.counterparty = resolvedEntityFromFollowup.value;
previousAnchorType = "counterparty";
previousAnchor = resolvedEntityFromFollowup.value;
resolvedCounterpartyFromDisplay = true;
} else if (resolvedEntityFromFollowup.entityType === "contract") {
previousFilters.contract = resolvedEntityFromFollowup.value;
previousAnchorType = "contract";
previousAnchor = resolvedEntityFromFollowup.value;
} else if (resolvedEntityFromFollowup.entityType === "item") {
previousFilters.item = resolvedEntityFromFollowup.value;
previousAnchorType = "item";
previousAnchor = resolvedEntityFromFollowup.value;
}
if (followupSelectionMode !== "switch_to_suggested_intent") {
followupSelectionMode = "carry_referenced_entity";
}
}
if (
!rootScopedPivot &&
!deps.toNonEmptyString(previousFilters.item) &&
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
sourceIntentHint === "inventory_sale_trace_for_item" ||
sourceIntentHint === "inventory_profitability_for_item" ||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
hasSelectedObjectInventorySignalPrimary ||
hasSelectedObjectInventorySignalAlternate)
) {
const selectedObjectLabel =
(navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null) ??
continuitySnapshot.activeItem ??
extractSelectedObjectLabel(userMessage) ??
(deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null);
if (selectedObjectLabel) {
previousFilters.item = selectedObjectLabel;
previousAnchorType = "item";
previousAnchor = selectedObjectLabel;
}
}
if (organizationClarificationSelection && !previousAnchor) {
previousAnchorType = "organization";
previousAnchor = organizationClarificationSelection;
}
if (
inventoryRootFrame &&
organizationClarificationSelection &&
!deps.toNonEmptyString(inventoryRootFrame.filters?.organization)
) {
inventoryRootFrame = {
...inventoryRootFrame,
filters: {
...(inventoryRootFrame.filters ?? {}),
organization: organizationClarificationSelection
}
};
}
if (!previousIntent && !previousAnchor && Object.keys(previousFilters).length === 0) {
return null;
}
const shouldAttachInventoryRootFrame = Boolean(
inventoryRootFrame &&
(rootScopedPivot ||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
hasNavigationInventoryItemFocusHint ||
inventoryShortFollowupPrimary ||
inventoryShortFollowupAlternate ||
hasInventoryRootTemporalFollowupPrimary ||
hasInventoryRootTemporalFollowupAlternate ||
hasInventoryRootRestatementPrimary ||
hasInventoryRootRestatementAlternate ||
hasSelectedObjectInventorySignalPrimary ||
hasSelectedObjectInventorySignalAlternate)
);
const carryoverTargetIntent =
inventoryPurchaseDateVatBridge
? "vat_liability_confirmed_for_tax_period"
: selectedObjectRetargetIntent &&
(explicitIntent === null ||
explicitIntent === "inventory_on_hand_as_of_date" ||
explicitIntent === sourceIntent)
? selectedObjectRetargetIntent
: followupSelectionMode === "carry_root_context"
? inventoryRootFrame?.intent ?? displayedEntityTargetIntent ?? explicitIntent ?? previousIntent ?? undefined
: explicitInventorySameDatePivot
? "inventory_on_hand_as_of_date"
: displayedEntityTargetIntent ?? explicitIntent ?? previousIntent ?? undefined;
return {
followupContext: {
previous_intent: previousIntent ?? undefined,
target_intent: carryoverTargetIntent,
previous_filters: previousFilters,
previous_anchor_type: previousAnchorType ?? undefined,
previous_anchor_value: previousAnchor,
resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined,
root_context_only: rootScopedPivot || undefined,
root_intent: shouldAttachInventoryRootFrame ? inventoryRootFrame?.intent ?? undefined : undefined,
root_filters: shouldAttachInventoryRootFrame ? inventoryRootFrame?.filters ?? undefined : undefined,
root_anchor_type: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorType ?? undefined : undefined,
root_anchor_value: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorValue ?? undefined : undefined,
current_frame_kind: shouldAttachInventoryRootFrame ? currentFrameKind ?? undefined : undefined
},
previousAddressIntent: previousIntent,
previousAddressAnchor: previousAnchor,
previousSourceIntent: sourceIntent,
followupSelectionMode,
hasImplicitContinuationSignal
};
}
function buildAddressDialogContinuationContractV2(
userMessage,
effectiveMessage,
carryoverMeta,
llmPreDecomposeMeta
) {
const sourceMessage = String(userMessage ?? "");
const canonicalMessage = String(effectiveMessage ?? sourceMessage);
const hasFollowupContext = Boolean(carryoverMeta?.followupContext);
const previousIntent = deps.toNonEmptyString(carryoverMeta?.previousSourceIntent) ?? null;
const selectionMode = deps.toNonEmptyString(carryoverMeta?.followupSelectionMode) ?? null;
const rootContextOnly = selectionMode === "carry_root_context";
const carryoverTargetIntent = deps.toNonEmptyString(carryoverMeta?.followupContext?.target_intent) ?? null;
const explicitIntentRaw = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const explicitIntent = explicitIntentRaw === "unknown" ? null : explicitIntentRaw;
const rootIntent = deps.toNonEmptyString(carryoverMeta?.followupContext?.root_intent) ?? null;
const targetIntent =
selectionMode === "switch_to_suggested_intent"
? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null
: rootContextOnly
? carryoverTargetIntent ?? rootIntent ?? explicitIntent ?? null
: carryoverTargetIntent ?? explicitIntent ?? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null;
const hasImplicitContinuationSignal = Boolean(carryoverMeta?.hasImplicitContinuationSignal);
const rewrittenByPredecompose =
deps.compactWhitespace(sourceMessage.toLowerCase()) !== deps.compactWhitespace(canonicalMessage.toLowerCase());
const hasExplicitIntent = Boolean(explicitIntent);
const decision = !hasFollowupContext
? "new_topic"
: selectionMode === "switch_to_suggested_intent"
? "switch_to_suggested"
: "continue_previous";
const reasons = [];
if (hasFollowupContext) {
reasons.push("followup_context_detected");
}
if (hasImplicitContinuationSignal) {
reasons.push("implicit_continuation_by_llm");
}
if (rewrittenByPredecompose) {
reasons.push("effective_message_rewritten_by_predecompose");
}
if (hasExplicitIntent) {
reasons.push("llm_contract_intent_available");
}
if (selectionMode === "carry_referenced_entity" && explicitIntent && previousIntent && explicitIntent !== previousIntent) {
reasons.push("operation_intent_from_current_message");
}
if (rootContextOnly) {
reasons.push("root_context_only_carryover");
}
return {
schema_version: "address_dialog_continuation_contract_v2",
source_message: sourceMessage,
effective_message: canonicalMessage,
decision,
decision_reasons: reasons,
followup_context_applied: hasFollowupContext,
previous_intent: previousIntent,
target_intent: targetIntent,
intent_selection_mode: selectionMode,
anchor_type: carryoverMeta?.followupContext?.previous_anchor_type ?? null,
anchor_value: carryoverMeta?.followupContext?.previous_anchor_value ?? null,
implicit_continuation_signal: hasImplicitContinuationSignal
};
}
return {
resolveAddressFollowupCarryoverContext,
buildAddressDialogContinuationContractV2
};
}