"use strict"; // @ts-nocheck Object.defineProperty(exports, "__esModule", { value: true }); exports.buildInventoryHistoryCapabilityFollowupReply = buildInventoryHistoryCapabilityFollowupReply; exports.buildAddressMemoryRecapReply = buildAddressMemoryRecapReply; exports.buildBroadBusinessEvaluationReply = buildBroadBusinessEvaluationReply; exports.buildConversationExecutiveSummaryReply = buildConversationExecutiveSummaryReply; 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+мы|executive\s+summary|финальн\w*\s+собери|итогов\w*\s+(?:резюм|summary|вывод)|по\s+всему\s+диалогу|где\s+ответы\s+были\s+подтвержден|где\s+proxy|где\s+прокси|не\s+хватил\w*\s+доказательств|ручн\w*\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 isLowQualityRecapCounterparty(value) { const normalized = normalizeRecapIdentity(value); if (!normalized) { return false; } const stopwordOnlyCounterparty = new Set([ "без", "в", "во", "для", "до", "за", "из", "к", "ко", "на", "от", "по", "с", "со", "у" ]); if (stopwordOnlyCounterparty.has(normalized)) { return true; } if (/^(?:и\s+)?(?:кто|что|где|какой|какие)\b/iu.test(normalized) || /(?:главн|основн|крупн|поставщик|клиент|контрагент|покупател|документ|движени|операци)/iu.test(normalized) && !/(?= 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 parseHumanMoney(value) { const text = String(value ?? "") .replace(/[^\d,.\-]/g, "") .replace(/\s+/g, "") .replace(",", "."); if (!text) { return null; } const parsed = Number(text); return Number.isFinite(parsed) ? parsed : null; } function pushBusinessLine(target, value) { const text = String(value ?? "").trim(); if (text && !target.includes(text)) { target.push(text); } } function collectBusinessEvaluationEvidence(input) { const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : []; const currentOrganizationKey = normalizeRecapIdentity(input.organization); const confirmedLines = []; const interpretationLines = []; let hasRanking = false; let hasNet = false; let moneySignalCount = 0; 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 debugOrganizationKey = normalizeRecapIdentity(debugContext.organization); if (currentOrganizationKey && debugOrganizationKey && debugOrganizationKey !== currentOrganizationKey) { continue; } const discoveryEntry = toRecordObject(item.debug.assistant_mcp_discovery_entry_point_v1); const bridge = toRecordObject(discoveryEntry?.bridge); const pilot = toRecordObject(bridge?.pilot); const pilotScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(item.debug, input.toNonEmptyString); if (!pilot) { continue; } 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 = input.toNonEmptyString(leader?.axis_value); const leaderAmount = input.toNonEmptyString(leader?.total_amount_human_ru); const period = input.toNonEmptyString(rankedFlow.period_scope); const periodPart = period ? ` за ${period}` : ""; if (leaderName && leaderAmount) { pushBusinessLine(confirmedLines, `Топ-контрагент по подтвержденному денежному срезу${periodPart}: ${leaderName} - ${leaderAmount}.`); hasRanking = true; moneySignalCount += 1; } } const bidirectionalFlow = toRecordObject(pilot.derived_bidirectional_value_flow); if (bidirectionalFlow) { const incoming = toRecordObject(bidirectionalFlow.incoming_customer_revenue); const outgoing = toRecordObject(bidirectionalFlow.outgoing_supplier_payout); const incomingAmount = input.toNonEmptyString(incoming?.total_amount_human_ru); const outgoingAmount = input.toNonEmptyString(outgoing?.total_amount_human_ru); const netAmount = input.toNonEmptyString(bidirectionalFlow.net_amount_human_ru); const period = input.toNonEmptyString(bidirectionalFlow.period_scope); const periodPart = period ? ` за ${period}` : ""; if (incomingAmount && outgoingAmount && netAmount) { pushBusinessLine(confirmedLines, `Денежный поток${periodPart}: получили ${incomingAmount}, заплатили ${outgoingAmount}, расчетное нетто ${netAmount}.`); const incomingNumber = parseHumanMoney(incomingAmount); const outgoingNumber = parseHumanMoney(outgoingAmount); const netNumber = parseHumanMoney(netAmount); if (incomingNumber && outgoingNumber !== null && netNumber !== null) { const spreadPercent = Math.abs(netNumber) / Math.max(Math.abs(incomingNumber), 1); const spreadLabel = `${Math.round(spreadPercent * 100)}%`; if (spreadPercent < 0.1) { pushBusinessLine(interpretationLines, `Обороты есть, но денежный спред узкий: нетто около ${spreadLabel} от входящего потока. Это не прибыль, но сигнал, что маржу надо проверять отдельно.`); } else if (netNumber > 0) { pushBusinessLine(interpretationLines, `Денежный поток в проверенном срезе положительный: нетто около ${spreadLabel} от входящего потока. Это хороший cash-flow сигнал, но не доказанная прибыль.`); } else { pushBusinessLine(interpretationLines, `Денежный поток в проверенном срезе отрицательный: исходящие платежи выше входящих примерно на ${spreadLabel} от входящего потока. Нужна проверка причин и структуры расходов.`); } } hasNet = true; moneySignalCount += 1; } } const valueFlow = toRecordObject(pilot.derived_value_flow); if (valueFlow) { const amount = input.toNonEmptyString(valueFlow.total_amount_human_ru); const period = input.toNonEmptyString(valueFlow.period_scope); const direction = String(valueFlow.value_flow_direction ?? ""); const periodPart = period ? ` за ${period}` : ""; if (amount) { pushBusinessLine(confirmedLines, direction === "outgoing_supplier_payout" ? `Исходящий денежный поток${periodPart}: ${amount}.` : `Входящий денежный поток${periodPart}: ${amount}.`); moneySignalCount += 1; } } const activityPeriod = toRecordObject(pilot.derived_activity_period); if (pilotScope === "counterparty_lifecycle_query_documents_v1" && activityPeriod) { const duration = input.toNonEmptyString(activityPeriod.duration_human_ru); const first = input.toNonEmptyString(activityPeriod.first_activity_date); const latest = input.toNonEmptyString(activityPeriod.latest_activity_date); if (duration) { pushBusinessLine(confirmedLines, `Подтвержденная активность в 1С: примерно ${duration}${first && latest ? ` (${first} - ${latest})` : ""}.`); } } if (pilotScope === "inventory_route_template_v1") { pushBusinessLine(confirmedLines, "Есть проверенный складской/товарный срез; его можно использовать как операционный контекст, но не как финансовую прибыль."); } } if (hasRanking) { pushBusinessLine(interpretationLines, "По клиентской базе уже виден лидер, но концентрацию выручки надо проверять отдельным рейтингом и долями, а не одной строкой."); } if (moneySignalCount > 0 && !hasNet) { pushBusinessLine(interpretationLines, "Денежный контур частично подтвержден, но без входящие-вс-исходящие нельзя честно говорить о чистом денежном эффекте."); } return { confirmedLines: confirmedLines.slice(0, 6), interpretationLines: interpretationLines.slice(0, 5), hasRanking, hasNet, moneySignalCount }; } 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}»` : ""; const businessEvidence = collectBusinessEvaluationEvidence({ sessionItems: input.sessionItems, organization, toNonEmptyString: input.toNonEmptyString }); const hasBusinessEvidence = businessEvidence.confirmedLines.length > 0; if (recapFacts.length > 0 || hasBusinessEvidence) { const moneyFactCount = recapFacts.filter((fact) => /(?:денежн|нетто|поступлен|платеж|рейтинг|клиент|выруч|оборот|заплатили|получили)/iu.test(fact)).length + businessEvidence.moneySignalCount; const hasRankingFact = businessEvidence.hasRanking || recapFacts.some((fact) => /(?:рейтинг|клиент|единственного клиента)/iu.test(fact)); const hasNetFact = businessEvidence.hasNet || recapFacts.some((fact) => /нетто/iu.test(fact)); const auditLines = [ moneyFactCount > 0 ? "- Денежный контур уже выглядит операционно значимым: есть подтвержденные поступления, платежи или клиентские срезы." : "- Операционная активность подтверждена, но денежный контур пока раскрыт слабо.", hasRankingFact ? "- По клиентской базе уже есть точечные лидеры, но это еще не полноценная управленческая сегментация всей базы." : "- Ключевых клиентов и концентрацию выручки стоит добрать отдельным рейтингом.", hasNetFact ? "- По нетто можно обсуждать направление денежного потока, но прибыль и маржу этим не доказываем." : "- Прибыль, маржа и качество операционки пока не доказаны: нужны расходы, себестоимость и задолженность." ]; return [ `Коротко: по уже подтвержденным срезам 1С${organizationPart} бизнес выглядит операционно живым; это предварительная оценка бизнеса, а для взрослого вывода еще нужны прибыль, маржа и долги.`, "Что уже видно:", ...recapFacts.map((fact) => `- ${ensureSentence(fact)}`), ...(businessEvidence.confirmedLines.length > 0 ? ["Подтвержденные метрики:", ...businessEvidence.confirmedLines.map((fact) => `- ${ensureSentence(fact)}`)] : []), "Предварительный LLM-аудит:", ...businessEvidence.interpretationLines.map((fact) => `- ${ensureSentence(fact)}`), ...auditLines, "Что добрать для полной оценки: обороты по годам, топ клиентов, входящие/исходящие деньги, дебиторку/кредиторку, НДС и признаки маржинальности." ].join("\n"); } return [ `Коротко: по нынешнему контексту 1С${organizationPart} я вижу признаки операционной активности, но для содержательной оценки бизнеса нужно еще несколько опорных срезов.`, "Если хочешь, я быстро доберу основу для такой оценки: денежный поток, дебиторка/кредиторка, НДС или ключевые контрагенты." ].join(" "); } function buildConversationExecutiveSummaryReply(input) { const contextFacts = (0, assistantContinuityPolicy_1.resolveAddressDebugContextFacts)(input.addressDebug, input.toNonEmptyString); const organization = input.organization ?? contextFacts.organization; const organizationPart = organization ? ` по компании «${organization}»` : ""; const recapFacts = collectRecentRecapFacts({ sessionItems: input.sessionItems, item: null, organization, toNonEmptyString: input.toNonEmptyString, limit: 8 }); const businessEvidence = collectBusinessEvaluationEvidence({ sessionItems: input.sessionItems, organization, toNonEmptyString: input.toNonEmptyString }); const confirmedLines = []; for (const fact of recapFacts) { pushBusinessLine(confirmedLines, ensureSentence(fact)); } for (const fact of businessEvidence.confirmedLines) { pushBusinessLine(confirmedLines, ensureSentence(fact)); } const proxyLines = []; for (const line of businessEvidence.interpretationLines) { pushBusinessLine(proxyLines, ensureSentence(line)); } if (businessEvidence.hasNet) { pushBusinessLine(proxyLines, "Нетто по деньгам можно использовать как cash-flow proxy, но это не бухгалтерская прибыль и не маржа."); } if (businessEvidence.hasRanking) { pushBusinessLine(proxyLines, "Крупнейшие клиенты/контрагенты видны как операционный сигнал концентрации, но это не полноценный CRM-аудит."); } const missingLines = [ "Чистая прибыль, финрезультат и маржа не доказаны без отдельной проверки себестоимости, расходов и закрытия периода.", "Просрочка, качество долга и due-date aging не доказаны без сроков оплаты и отдельного долгового контура.", "Ликвидность склада, резервы, списания и устаревание нельзя считать подтвержденными без специальных складских доказательств.", "Vendor-risk, качество закупок и юридическая надежность контрагентов остаются вне подтвержденного контура." ]; const manualLines = [ "Сверить ОСВ/финрезультат и управленческие расходы за ключевые годы.", "Отдельно проверить старые открытые расчеты, крупные долги и документы закрытия.", "Посмотреть концентрацию клиентов и поставщиков не только по сумме, но и по доле в периоде.", "Сверить НДС, склад и договоры там, где ответ опирался на proxy, а не на прямой учетный факт." ]; const confirmedSection = confirmedLines.length > 0 ? confirmedLines.slice(0, 10).map((line) => `- ${line}`) : ["- Есть grounded-контекст диалога, но для строгого executive summary не хватает подтвержденных метрик в текущем окне."]; const proxySection = proxyLines.length > 0 ? proxyLines.slice(0, 6).map((line) => `- ${line}`) : ["- Proxy-выводы пока слабые: можно говорить только о направлении проверки, не о зрелой оценке бизнеса."]; return [ `Executive summary${organizationPart}: по диалогу уже можно собрать рабочую карту подтвержденного, proxy и ручного контроля, но не стоит выдавать это за полный аудит компании.`, "Подтверждено по данным/ответам 1С:", ...confirmedSection, "Proxy и осторожная аналитика:", ...proxySection, "Где не хватило доказательств:", ...missingLines.map((line) => `- ${line}`), "Что директору смотреть руками в первую очередь:", ...manualLines.map((line) => `- ${line}`) ].join("\n"); } 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 }; }