"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 readDiscoveryMetadataScope(debug) { const discoveryEntry = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1); const bridge = toRecordObject(discoveryEntry?.bridge); const pilot = toRecordObject(bridge?.pilot); const surface = toRecordObject(pilot?.derived_metadata_surface); const surfaceScope = toNonEmptyString(surface?.metadata_scope); if (surfaceScope) { return surfaceScope; } const turnInput = toRecordObject(discoveryEntry?.turn_input); const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref); const entityCandidates = Array.isArray(turnMeaningRef?.explicit_entity_candidates) ? turnMeaningRef.explicit_entity_candidates : []; for (const candidate of entityCandidates) { const text = toNonEmptyString(candidate); if (text) { return text; } } return null; } function buildDiscoveryRecapFactLine(input) { if (!input.debug) { 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 === "metadata_inspection_v1") { const metadataScope = readDiscoveryMetadataScope(input.debug); const surface = toRecordObject(pilot?.derived_metadata_surface); const entitySets = Array.isArray(surface?.available_entity_sets) ? surface.available_entity_sets .map((item) => toNonEmptyString(item)) .filter((item) => Boolean(item)) : []; const fields = Array.isArray(surface?.available_fields) ? surface.available_fields .map((item) => toNonEmptyString(item)) .filter((item) => Boolean(item)) : []; const objects = Array.isArray(surface?.matched_objects) ? surface.matched_objects .map((item) => toNonEmptyString(item)) .filter((item) => Boolean(item)) : []; const rows = Number(surface?.matched_rows ?? 0); const scopePart = metadataScope ? ` по области «${metadataScope}»` : ""; const objectsPart = objects.length > 0 ? `, нашли объекты ${objects.slice(0, 4).join(", ")}` : ""; const entitySetsPart = entitySets.length > 0 ? `, видны типы ${entitySets.slice(0, 4).join(", ")}` : ""; const fieldsPart = fields.length > 0 ? `, доступны поля/секции ${fields.slice(0, 5).join(", ")}` : ""; return `смотрели схему 1С${scopePart}${periodPart}: ${rows} подтвержденных строк${objectsPart}${entitySetsPart}${fieldsPart}`.trim(); } const rankedFlow = toRecordObject(pilot?.derived_ranked_value_flow); if (rankedFlow) { const rankedValues = Array.isArray(rankedFlow.ranked_values) ? rankedFlow.ranked_values : []; const leader = toRecordObject(rankedValues[0]); const leaderName = toNonEmptyString(leader?.axis_value); const leaderAmount = toNonEmptyString(leader?.total_amount_human_ru); const leaderRows = toNonEmptyString(leader?.rows_with_amount); const organization = toNonEmptyString(rankedFlow.organization_scope) ?? input.organization; const period = toNonEmptyString(rankedFlow.period_scope) ?? input.scopedDate; const organizationPart = organization ? ` по компании «${organization}»` : ""; const periodPartForRanking = period ? ` за период ${period}` : periodPart; if (leaderName && leaderAmount) { const rowsPart = leaderRows ? ` по ${leaderRows} строкам` : ""; const rankingKind = rankedValues.length > 1 ? "строили рейтинг клиентов" : "видели единственного клиента в проверенном срезе"; return `${rankingKind}${organizationPart}${periodPartForRanking}: ${leaderName} — ${leaderAmount}${rowsPart}`.trim(); } } const subjectPart = input.counterparty ? `контрагенту «${input.counterparty}»` : input.organization ? `компании «${input.organization}»` : null; if (!subjectPart) { return null; } if (pilotScope === "counterparty_lifecycle_query_documents_v1") { const activityPeriod = toRecordObject(pilot?.derived_activity_period); const duration = toNonEmptyString(activityPeriod?.duration_human_ru); return duration ? `смотрели подтвержденную активность по ${subjectPart}${periodPart} и оценили период взаимодействия примерно как ${duration}` : `смотрели подтвержденную активность по ${subjectPart}${periodPart}`; } if (pilotScope === "counterparty_supplier_payout_query_movements_v1") { const flow = toRecordObject(pilot?.derived_value_flow); const amount = toNonEmptyString(flow?.total_amount_human_ru); const flowPeriodPart = periodPartForRecap(toNonEmptyString(flow?.period_scope) ?? input.scopedDate); return amount ? `считали исходящие платежи/списания по ${subjectPart}${flowPeriodPart}: ${amount}` : `считали исходящие платежи/списания по ${subjectPart}${flowPeriodPart}`; } if (pilotScope === "counterparty_value_flow_query_movements_v1") { const flow = toRecordObject(pilot?.derived_value_flow); const amount = toNonEmptyString(flow?.total_amount_human_ru); const flowPeriodPart = periodPartForRecap(toNonEmptyString(flow?.period_scope) ?? input.scopedDate); return amount ? `смотрели денежный поток по ${subjectPart}${flowPeriodPart}: ${amount}` : `смотрели денежный поток по ${subjectPart}${flowPeriodPart}`; } 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); const flowPeriodPart = periodPartForRecap(toNonEmptyString(flow?.period_scope) ?? input.scopedDate); if (incomingAmount && outgoingAmount && netAmount) { return `считали нетто по деньгам по ${subjectPart}${flowPeriodPart}: получили ${incomingAmount}, заплатили ${outgoingAmount}, расчетное нетто ${netAmount}`; } return `считали нетто по деньгам по ${subjectPart}${flowPeriodPart}`; } 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, organization: input.organization, 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 >= (input.limit ?? 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 (recapFacts.length > 0) { return [ "Да, помню. В предыдущем проверенном контуре мы уже выяснили:", ...recapFacts.map((fact) => `- ${fact}.`), "Могу продолжить от этого места: углубиться в данные, документы, движения или границы подтверждения." ].join("\n"); } 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, limit: 5 }); const organizationPart = organization ? ` по компании «${organization}»` : ""; if (recapFacts.length > 0) { const moneyFactCount = recapFacts.filter((fact) => /(?:денежн|нетто|поступлен|платеж|рейтинг|клиент|выруч|оборот|заплатили|получили)/iu.test(fact)).length; const hasRankingFact = recapFacts.some((fact) => /(?:рейтинг|клиент|единственного клиента)/iu.test(fact)); const hasNetFact = recapFacts.some((fact) => /нетто/iu.test(fact)); const auditLines = [ moneyFactCount > 0 ? "- Денежный контур уже выглядит операционно значимым: есть подтвержденные поступления, платежи или клиентские срезы." : "- Операционная активность подтверждена, но денежный контур пока раскрыт слабо.", hasRankingFact ? "- По клиентской базе уже есть точечные лидеры, но это еще не полноценная управленческая сегментация всей базы." : "- Ключевых клиентов и концентрацию выручки стоит добрать отдельным рейтингом.", hasNetFact ? "- По нетто можно обсуждать направление денежного потока, но прибыль и маржу этим не доказываем." : "- Прибыль, маржа и качество операционки пока не доказаны: нужны расходы, себестоимость и задолженность." ]; return [ `Коротко: по уже подтвержденным срезам 1С${organizationPart} компания выглядит операционно живой; это предварительная оценка бизнеса, а для взрослого вывода еще нужны прибыль, маржа и долги.`, "Что уже видно:", ...recapFacts.map((fact) => `- ${ensureSentence(fact)}`), "Предварительный LLM-аудит:", ...auditLines, "Что добрать для полной оценки: обороты по годам, топ клиентов, входящие/исходящие деньги, дебиторку/кредиторку, НДС и признаки маржинальности." ].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 }; }