"use strict"; // @ts-nocheck Object.defineProperty(exports, "__esModule", { value: true }); exports.buildInventoryHistoryCapabilityFollowupReply = buildInventoryHistoryCapabilityFollowupReply; exports.buildAddressMemoryRecapReply = buildAddressMemoryRecapReply; exports.buildBroadBusinessEvaluationReply = buildBroadBusinessEvaluationReply; exports.buildSelectedObjectAnswerInspectionReply = buildSelectedObjectAnswerInspectionReply; exports.resolveAssistantLivingChatMemoryContext = resolveAssistantLivingChatMemoryContext; exports.createAssistantMemoryRecapPolicy = createAssistantMemoryRecapPolicy; const assistantContinuityPolicy_1 = require("./assistantContinuityPolicy"); function toNonEmptyString(value) { if (value === null || value === undefined) { return null; } const text = String(value).trim(); return text.length > 0 ? text : null; } function toRecordObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value; } function ensureSentence(value) { const text = String(value ?? "").trim(); if (!text) { return ""; } return /[.!?]$/.test(text) ? text : `${text}.`; } function periodPartForRecap(scopedDate) { if (!scopedDate) { return ""; } return /^\d{2}\.\d{2}\.\d{4}$/.test(scopedDate) ? ` на ${scopedDate}` : ` за период ${scopedDate}`; } function buildDiscoveryRecapFactLine(input) { if (!input.debug || !input.counterparty) { return null; } const pilotScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(input.debug, toNonEmptyString); const discoveryEntry = toRecordObject(input.debug.assistant_mcp_discovery_entry_point_v1); const bridge = toRecordObject(discoveryEntry?.bridge); const pilot = toRecordObject(bridge?.pilot); const periodPart = periodPartForRecap(input.scopedDate); if (pilotScope === "counterparty_lifecycle_query_documents_v1") { const activityPeriod = toRecordObject(pilot?.derived_activity_period); const duration = toNonEmptyString(activityPeriod?.duration_human_ru); return duration ? `смотрели подтвержденную активность по контрагенту «${input.counterparty}»${periodPart} и оценили период взаимодействия примерно как ${duration}` : `смотрели подтвержденную активность по контрагенту «${input.counterparty}»${periodPart}`; } if (pilotScope === "counterparty_supplier_payout_query_movements_v1") { const flow = toRecordObject(pilot?.derived_value_flow); const amount = toNonEmptyString(flow?.total_amount_human_ru); return amount ? `считали исходящие платежи/списания по контрагенту «${input.counterparty}»${periodPart}: ${amount}` : `считали исходящие платежи/списания по контрагенту «${input.counterparty}»${periodPart}`; } if (pilotScope === "counterparty_value_flow_query_movements_v1") { const flow = toRecordObject(pilot?.derived_value_flow); const amount = toNonEmptyString(flow?.total_amount_human_ru); return amount ? `смотрели денежный поток по контрагенту «${input.counterparty}»${periodPart}: ${amount}` : `смотрели денежный поток по контрагенту «${input.counterparty}»${periodPart}`; } if (pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1") { const flow = toRecordObject(pilot?.derived_bidirectional_value_flow); const incoming = toRecordObject(flow?.incoming_customer_revenue); const outgoing = toRecordObject(flow?.outgoing_supplier_payout); const incomingAmount = toNonEmptyString(incoming?.total_amount_human_ru); const outgoingAmount = toNonEmptyString(outgoing?.total_amount_human_ru); const netAmount = toNonEmptyString(flow?.net_amount_human_ru); if (incomingAmount && outgoingAmount && netAmount) { return `считали нетто по деньгам с контрагентом «${input.counterparty}»${periodPart}: получили ${incomingAmount}, заплатили ${outgoingAmount}, расчетное нетто ${netAmount}`; } return `считали нетто по деньгам с контрагентом «${input.counterparty}»${periodPart}`; } return null; } function collectMessageSamples(input) { const values = [ input.rawUserMessage, input.repairedRawUserMessage, input.effectiveAddressUserMessage, input.repairedEffectiveAddressUserMessage ]; return Array.from(new Set(values .map((item) => String(item ?? "").trim()) .filter((item) => item.length > 0))); } function hasSignalAcrossSamples(samples, detector) { return samples.some((sample) => detector(sample)); } function hasExplicitRecapPromptSignal(samples) { return samples.some((sample) => /(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|что\s+уже\s+поняли|напомни\s+что\s+мы)/iu.test(sample)); } function buildInventoryHistoryCapabilityFollowupReply(input) { const contextFacts = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.addressDebug, input.toNonEmptyString); const organization = input.organization ?? contextFacts.organization; const lastAsOfDate = contextFacts.scopedDate; const organizationPart = organization ? ` по компании «${organization}»` : ""; const referenceLine = lastAsOfDate ? `Да, могу. Сейчас мы уже смотрели складской срез${organizationPart} на ${lastAsOfDate}.` : `Да, могу показать исторические данные${organizationPart} в этом же складском контуре.`; return [ referenceLine, `Могу показать исторические остатки${organizationPart} за нужный месяц, дату или год.`, "Например:", "- `на март 2020`", "- `на июнь 2016`", "- `за 2017 год`", "- `сравни июнь 2016 с текущим срезом`", "Если хочешь, сразу покажу нужный исторический период." ].join("\n"); } function normalizeRecapIdentity(value) { return String(value ?? "") .trim() .toLowerCase() .replace(/[«»"'`]/g, "") .replace(/\s+/g, " "); } function buildRecapFactLine(input) { const detectedIntent = String(input.debug?.detected_intent ?? ""); const scopedDate = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.debug).scopedDate; const discoveryFact = buildDiscoveryRecapFactLine({ debug: input.debug, counterparty: input.counterparty, scopedDate }); if (discoveryFact) { return discoveryFact; } const itemPart = input.item ? `по позиции «${input.item}»` : null; const counterpartyPart = input.counterparty ? `по контрагенту «${input.counterparty}»` : null; const organizationPart = input.organization ? `по компании «${input.organization}»` : null; const datePart = scopedDate ? ` на ${scopedDate}` : ""; if (detectedIntent === "inventory_on_hand_as_of_date") { return `смотрели остатки${organizationPart ? ` ${organizationPart}` : ""}${datePart}`.trim(); } if (detectedIntent === "inventory_purchase_provenance_for_item" && itemPart) { return `разобрали, кто поставлял ${itemPart}${datePart}`.trim(); } if (detectedIntent === "inventory_purchase_documents_for_item" && itemPart) { return `подняли документы закупки ${itemPart}${datePart}`.trim(); } if (detectedIntent === "inventory_sale_trace_for_item" && itemPart) { return `разобрали, кому продавали ${itemPart}${datePart}`.trim(); } if (detectedIntent === "inventory_purchase_to_sale_chain" && itemPart) { return `проследили цепочку от закупки до продажи ${itemPart}${datePart}`.trim(); } if (detectedIntent === "inventory_profitability_for_item" && itemPart) { return `смотрели рентабельность ${itemPart}${datePart}`.trim(); } if (detectedIntent === "inventory_aging_by_purchase_date" && itemPart) { return `смотрели возраст остатков ${itemPart}${datePart}`.trim(); } if (detectedIntent === "counterparty_activity_lifecycle" && organizationPart) { return `смотрели активность в базе 1С ${organizationPart}`.trim(); } if (detectedIntent === "list_documents_by_counterparty" && counterpartyPart) { return `поднимали документы ${counterpartyPart}${datePart}`.trim(); } if (detectedIntent === "list_documents_by_counterparty" && organizationPart) { return `поднимали документы ${organizationPart}${datePart}`.trim(); } return null; } function collectRecentRecapFacts(input) { const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : []; if (sessionItems.length === 0) { return []; } const currentItemKey = normalizeRecapIdentity(input.item); const currentOrganizationKey = normalizeRecapIdentity(input.organization); const facts = []; const seen = new Set(); for (let index = sessionItems.length - 1; index >= 0; index -= 1) { const item = sessionItems[index]; if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") { continue; } if (!(0, assistantContinuityPolicy_1.isGroundedAddressDebug)(item.debug, input.toNonEmptyString)) { continue; } const debugContext = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(item.debug, input.toNonEmptyString); const debugItem = debugContext.item; const debugOrganization = debugContext.organization; const itemMatches = currentItemKey ? normalizeRecapIdentity(debugItem) === currentItemKey : false; const organizationMatches = currentOrganizationKey ? normalizeRecapIdentity(debugOrganization) === currentOrganizationKey : false; if (currentItemKey && !itemMatches) { continue; } if (!currentItemKey && currentOrganizationKey && !organizationMatches) { continue; } const fact = buildRecapFactLine({ debug: item.debug, item: debugItem, counterparty: debugContext.counterparty, organization: debugOrganization }); if (!fact || seen.has(fact)) { continue; } seen.add(fact); facts.push(fact); if (facts.length >= 3) { break; } } return facts.reverse(); } function buildAddressMemoryRecapReply(input) { const contextFacts = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.addressDebug, input.toNonEmptyString); const item = contextFacts.item; const counterparty = contextFacts.counterparty; const organization = input.organization ?? contextFacts.organization; const scopedDate = contextFacts.scopedDate; const recapFacts = collectRecentRecapFacts({ sessionItems: input.sessionItems, item, organization, toNonEmptyString: input.toNonEmptyString }); if (item) { if (recapFacts.length > 0) { const datePart = scopedDate ? ` в срезе на ${scopedDate}` : ""; const organizationPart = organization ? ` по компании «${organization}»` : ""; return [ `Да, помню. По позиции «${item}»${organizationPart}${datePart} мы уже выяснили:`, ...recapFacts.map((fact) => `- ${fact}.`), "Могу сразу продолжить по ней: поставщик, закупка, документы или продажа." ].join("\n"); } const datePart = scopedDate ? ` в срезе на ${scopedDate}` : ""; const organizationPart = organization ? ` по компании «${organization}»` : ""; return [ `Да, помню. Мы обсуждали позицию «${item}»${organizationPart}${datePart}.`, "Могу продолжить по ней без переписывания сущности: кто поставил, когда купили, по каким документам или кому продали." ].join(" "); } if (counterparty) { const organizationPart = organization ? ` по компании «${organization}»` : ""; const periodPart = periodPartForRecap(scopedDate); if (recapFacts.length > 0) { return [ `Да, помню. По контрагенту «${counterparty}»${organizationPart}${periodPart} мы уже выяснили:`, ...recapFacts.map((fact) => `- ${fact}.`), "Могу сразу продолжить по нему: поступления, платежи, нетто, помесячную раскладку или границы подтверждения." ].join("\n"); } return [ `Да, помню. Мы уже смотрели контур по контрагенту «${counterparty}»${organizationPart}${periodPart}.`, "Могу продолжить по нему без переписывания контекста: поступления, платежи, нетто, документы или пояснение границ ответа." ].join(" "); } if (organization || scopedDate) { const organizationPart = organization ? ` по компании «${organization}»` : ""; const datePart = scopedDate ? ` на ${scopedDate}` : ""; return [ `Да, помню. Мы уже смотрели адресный контур${organizationPart}${datePart}.`, "Могу кратко напомнить контекст или сразу продолжить следующий шаг по этому же сценарию." ].join(" "); } return "Да, помню предыдущий адресный контур. Могу кратко напомнить, что мы уже подтвердили, или сразу продолжить следующий шаг."; } function buildBroadBusinessEvaluationReply(input) { const contextFacts = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.addressDebug, input.toNonEmptyString); const organization = input.organization ?? contextFacts.organization; const recapFacts = collectRecentRecapFacts({ sessionItems: input.sessionItems, item: null, organization, toNonEmptyString: input.toNonEmptyString }); const organizationPart = organization ? ` по компании «${organization}»` : ""; if (recapFacts.length > 0) { return [ `Коротко: по тому, что мы уже подтвердили в 1С${organizationPart}, компания выглядит операционно живой, но это пока только частичная оценка бизнеса.`, "Сейчас я опираюсь на такие подтвержденные факты:", ...recapFacts.map((fact) => `- ${ensureSentence(fact)}`), "Это еще не полная диагностика всего бизнеса и не вывод о прибыли: я честно суммирую только те контуры, которые мы уже проверили в диалоге.", "Если хочешь, следующим шагом могу сузить оценку до денежного потока, долгов, НДС или ключевых контрагентов." ].join("\n"); } return [ `Коротко: по нынешнему контексту 1С${organizationPart} я вижу признаки операционной активности, но для содержательной оценки бизнеса нужно еще несколько опорных срезов.`, "Если хочешь, я быстро доберу основу для такой оценки: денежный поток, дебиторка/кредиторка, НДС или ключевые контрагенты." ].join(" "); } function buildSelectedObjectAnswerInspectionReply(input) { const contextFacts = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.addressDebug, input.toNonEmptyString); const itemLabel = contextFacts.item ?? "эта позиция"; const counterpartyLabel = contextFacts.counterparty; const detectedIntent = String(input.addressDebug?.detected_intent ?? ""); const pilotScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(input.addressDebug, input.toNonEmptyString); const periodPart = periodPartForRecap(contextFacts.scopedDate); if (counterpartyLabel && pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1") { return [ `Да, в предыдущем ответе речь шла о двустороннем денежном потоке с контрагентом «${counterpartyLabel}»${periodPart}.`, "Нетто там означало разницу между тем, что получили, и тем, что заплатили по найденным строкам 1С.", "Это расчет по проверенному периоду и подтвержденным строкам, а не заявление про весь оборот вне этого окна." ].join(" "); } if (counterpartyLabel && pilotScope === "counterparty_supplier_payout_query_movements_v1") { return [ `Да, в предыдущем ответе речь шла об исходящих платежах/списаниях по контрагенту «${counterpartyLabel}»${periodPart}.`, "Это сумма по найденным строкам 1С за проверенный период, а не обещание, что за пределами этого окна больше движений не было." ].join(" "); } if (counterpartyLabel && pilotScope === "counterparty_value_flow_query_movements_v1") { return [ `Да, в предыдущем ответе речь шла о денежном потоке по контрагенту «${counterpartyLabel}»${periodPart}.`, "Это расчет по найденным движениям 1С за проверенный период, а не безусловный итог по всем временам." ].join(" "); } if (counterpartyLabel && pilotScope === "counterparty_lifecycle_query_documents_v1") { return [ `Да, в предыдущем ответе речь шла об активности контрагента «${counterpartyLabel}»${periodPart}.`, "Это оценка по подтвержденным строкам 1С, а не юридически подтвержденная дата регистрации." ].join(" "); } if (detectedIntent === "inventory_sale_trace_for_item") { return [ `Да, если так прозвучало, это ошибка чтения ответа. «${itemLabel}» здесь не контрагент, а сама позиция, по которой мы смотрели продажу.`, "В предыдущем ответе я показывал документы выбытия по этой позиции. Покупатель в доступных данных отдельно не выделен, поэтому назвать контрагента-покупателя я там не мог.", "Если хочешь, следующим шагом могу отдельно проверить, можно ли вытащить покупателя по связанным документам реализации." ].join(" "); } if (detectedIntent === "inventory_purchase_provenance_for_item" || detectedIntent === "inventory_purchase_documents_for_item") { return [ `Да, если так прозвучало, это ошибка чтения ответа. «${itemLabel}» здесь не контрагент, а сама позиция или номенклатура.`, "В предыдущем ответе речь шла о закупке этой позиции: я перечислял поставщиков или закупочные документы по ней, а не называл саму позицию контрагентом." ].join(" "); } return [ `Да, если так прозвучало, это ошибка чтения ответа. «${itemLabel}» здесь не контрагент, а выбранный объект разбора.`, "Я сейчас уточняю именно смысл предыдущего grounded-ответа по этой позиции, а не запускаю новый адресный поиск." ].join(" "); } function resolveAssistantLivingChatMemoryContext(input) { const contextualInventoryHistoryCapabilityFollowup = String(input.modeDecisionReason ?? "") === "inventory_history_capability_followup_detected"; const contextualMemoryRecapFollowup = String(input.modeDecisionReason ?? "") === "memory_recap_followup_detected"; const contextualAnswerInspectionFollowup = String(input.modeDecisionReason ?? "") === "answer_inspection_followup_detected"; const continuity = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({ sessionItems: input.sessionItems, toNonEmptyString }); return { contextualInventoryHistoryCapabilityFollowup, contextualMemoryRecapFollowup, contextualAnswerInspectionFollowup, lastGroundedInventoryAddressDebug: contextualInventoryHistoryCapabilityFollowup ? continuity.lastGroundedInventoryAddressDebug : null, lastMemoryAddressDebug: contextualMemoryRecapFollowup ? continuity.lastGroundedItemAddressDebug ?? continuity.lastGroundedAddressDebug : null, lastAnswerInspectionAddressDebug: contextualAnswerInspectionFollowup ? continuity.lastGroundedItemAddressDebug ?? continuity.lastGroundedAddressDebug : null }; } function createAssistantMemoryRecapPolicy(deps) { function resolveRouteMemorySignals(input) { const samples = collectMessageSamples(input); const continuity = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({ sessionItems: input.sessionItems, toNonEmptyString }); const groundedInventoryContext = continuity.lastGroundedInventoryAddressDebug ?? input.lastGroundedAddressDebug; const historicalCapabilitySignal = hasSignalAcrossSamples(samples, deps.hasHistoricalCapabilityFollowupSignal); const memoryRecapSignal = hasSignalAcrossSamples(samples, deps.hasConversationMemoryRecallFollowupSignal); const explicitRecapPromptSignal = hasExplicitRecapPromptSignal(samples); return { contextualHistoricalCapabilityFollowupDetected: Boolean(input.capabilityMetaQuery && !input.dataScopeMetaQuery && !input.dataRetrievalSignal && historicalCapabilitySignal && deps.isGroundedInventoryContextDebug(groundedInventoryContext)), contextualMemoryRecapFollowupDetected: Boolean(!input.dataScopeMetaQuery && !input.capabilityMetaQuery && !input.aggregateBusinessAnalyticsSignal && memoryRecapSignal && (explicitRecapPromptSignal || (!input.dataRetrievalSignal && !input.strongDataSignal)) && continuity.hasGroundedAddressContext) }; } return { resolveRouteMemorySignals }; }