Архитектура: централизовать selected-item carryover в continuity policy и transition glue

This commit is contained in:
2026-04-19 12:13:43 +03:00
parent 4f56aa9b2a
commit 851289b9a6
7 changed files with 179 additions and 38 deletions
@@ -16,6 +16,7 @@ exports.applyTemporalCarryoverFilters = applyTemporalCarryoverFilters;
exports.applyOrganizationCarryoverFilters = applyOrganizationCarryoverFilters;
exports.applyHistoricalPartyCarryoverFilters = applyHistoricalPartyCarryoverFilters;
exports.applyReferencedEntityCarryover = applyReferencedEntityCarryover;
exports.applySelectedItemCarryover = applySelectedItemCarryover;
exports.buildInventoryRootFrameFromAddressDebug = buildInventoryRootFrameFromAddressDebug;
exports.isGroundedAddressDebug = isGroundedAddressDebug;
exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot;
@@ -352,6 +353,33 @@ function applyReferencedEntityCarryover(previousFilters, previousAnchorType, pre
resolvedCounterpartyFromDisplay
};
}
function applySelectedItemCarryover(previousFilters, previousAnchorType, previousAnchorValue, rootScopedPivot, shouldApplySelectedItemCarryover, navigationFocusItemLabel, continuityActiveItem, selectedObjectLabelFromUser, selectedObjectLabelFromAlternate, toNonEmptyString = fallbackToNonEmptyString) {
const nextFilters = previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {};
if (rootScopedPivot || !shouldApplySelectedItemCarryover || toNonEmptyString(nextFilters.item)) {
return {
previousFilters: nextFilters,
previousAnchorType,
previousAnchorValue
};
}
const selectedObjectLabel = toNonEmptyString(navigationFocusItemLabel) ??
toNonEmptyString(continuityActiveItem) ??
toNonEmptyString(selectedObjectLabelFromUser) ??
toNonEmptyString(selectedObjectLabelFromAlternate);
if (!selectedObjectLabel) {
return {
previousFilters: nextFilters,
previousAnchorType,
previousAnchorValue
};
}
nextFilters.item = selectedObjectLabel;
return {
previousFilters: nextFilters,
previousAnchorType: "item",
previousAnchorValue: selectedObjectLabel
};
}
function buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString = fallbackToNonEmptyString) {
if (!debug || typeof debug !== "object") {
return null;
@@ -667,27 +667,19 @@ function createAssistantTransitionPolicy(deps) {
followupSelectionMode,
resolvedCounterpartyFromDisplay
} = (0, assistantContinuityPolicy_1.applyReferencedEntityCarryover)(previousFilters, previousAnchorType, previousAnchor, followupSelectionMode, resolvedEntityFromFollowup, rootScopedPivot, deps.toNonEmptyString));
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;
}
}
({
previousFilters,
previousAnchorType,
previousAnchorValue: previousAnchor
} = (0, assistantContinuityPolicy_1.applySelectedItemCarryover)(previousFilters, previousAnchorType, previousAnchor, rootScopedPivot, 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, navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null, continuitySnapshot.activeItem, extractSelectedObjectLabel(userMessage), deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null, deps.toNonEmptyString));
if (explicitOrganizationClarificationSelection && !previousAnchor) {
previousAnchorType = "organization";
previousAnchor = explicitOrganizationClarificationSelection;
@@ -536,6 +536,53 @@ export function applyReferencedEntityCarryover(
};
}
export function applySelectedItemCarryover(
previousFilters: Record<string, unknown> | null,
previousAnchorType: string | null,
previousAnchorValue: string | null,
rootScopedPivot: boolean,
shouldApplySelectedItemCarryover: boolean,
navigationFocusItemLabel: unknown,
continuityActiveItem: unknown,
selectedObjectLabelFromUser: unknown,
selectedObjectLabelFromAlternate: unknown,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
): {
previousFilters: Record<string, unknown>;
previousAnchorType: string | null;
previousAnchorValue: string | null;
} {
const nextFilters =
previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {};
if (rootScopedPivot || !shouldApplySelectedItemCarryover || toNonEmptyString(nextFilters.item)) {
return {
previousFilters: nextFilters,
previousAnchorType,
previousAnchorValue
};
}
const selectedObjectLabel =
toNonEmptyString(navigationFocusItemLabel) ??
toNonEmptyString(continuityActiveItem) ??
toNonEmptyString(selectedObjectLabelFromUser) ??
toNonEmptyString(selectedObjectLabelFromAlternate);
if (!selectedObjectLabel) {
return {
previousFilters: nextFilters,
previousAnchorType,
previousAnchorValue
};
}
nextFilters.item = selectedObjectLabel;
return {
previousFilters: nextFilters,
previousAnchorType: "item",
previousAnchorValue: selectedObjectLabel
};
}
export function buildInventoryRootFrameFromAddressDebug(
debug: Record<string, unknown> | null,
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
@@ -3,6 +3,7 @@ import {
applyHistoricalPartyCarryoverFilters,
applyOrganizationCarryoverFilters,
applyReferencedEntityCarryover,
applySelectedItemCarryover,
applyTemporalCarryoverFilters,
buildRootScopedCarryoverFilters,
buildInventoryRootFrameFromAddressDebug,
@@ -874,10 +875,16 @@ export function createAssistantTransitionPolicy(deps) {
rootScopedPivot,
deps.toNonEmptyString
));
if (
!rootScopedPivot &&
!deps.toNonEmptyString(previousFilters.item) &&
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
({
previousFilters,
previousAnchorType,
previousAnchorValue: previousAnchor
} = applySelectedItemCarryover(
previousFilters,
previousAnchorType,
previousAnchor,
rootScopedPivot,
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" ||
@@ -885,19 +892,13 @@ export function createAssistantTransitionPolicy(deps) {
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;
}
}
hasSelectedObjectInventorySignalAlternate,
navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null,
continuitySnapshot.activeItem,
extractSelectedObjectLabel(userMessage),
deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null,
deps.toNonEmptyString
));
if (explicitOrganizationClarificationSelection && !previousAnchor) {
previousAnchorType = "organization";
previousAnchor = explicitOrganizationClarificationSelection;
@@ -3,6 +3,7 @@ import {
applyHistoricalPartyCarryoverFilters,
applyOrganizationCarryoverFilters,
applyReferencedEntityCarryover,
applySelectedItemCarryover,
applyTemporalCarryoverFilters,
buildRootScopedCarryoverFilters,
hydrateInventoryRootFrameState,
@@ -324,4 +325,29 @@ describe("assistantContinuityPolicy organization authority", () => {
resolvedCounterpartyFromDisplay: true
});
});
it("applies selected-item carryover from navigation focus before continuity and explicit labels", () => {
const carryover = applySelectedItemCarryover(
{
organization: "Org Alt"
},
null,
null,
false,
true,
"Workstation Navigation",
"Workstation Continuity",
"Workstation User",
"Workstation Alternate"
);
expect(carryover).toEqual({
previousFilters: {
organization: "Org Alt",
item: "Workstation Navigation"
},
previousAnchorType: "item",
previousAnchorValue: "Workstation Navigation"
});
});
});
@@ -297,6 +297,48 @@ describe("assistantTransitionPolicy", () => {
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
});
it("hydrates selected-item carryover through shared continuity helper for short object follow-up", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: "Подтвержден складской срез по выбранной позиции.",
debug: {
detected_intent: "inventory_on_hand_as_of_date",
extracted_filters: {
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2020-03-31"
}
}
}),
hasAddressFollowupContextSignal: () => true,
hasShortInventoryObjectFollowupSignal: () => true,
findRecentInventoryRootFrame: () => null,
resolveAddressIntent: () => ({ intent: "unknown" })
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"по этой позиции покажи документы",
[],
null,
null,
{
session_context: {
active_focus_object: {
object_type: "item",
label: "Workstation Focus"
}
}
}
);
expect(carryover?.followupContext?.previous_filters).toMatchObject({
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2020-03-31",
item: "Workstation Focus"
});
expect(carryover?.followupContext?.previous_anchor_type).toBe("item");
expect(carryover?.followupContext?.previous_anchor_value).toBe("Workstation Focus");
});
it("hydrates follow-up organization from shared assistant authority when local history filters are empty", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({