"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.inferP0DomainFromMessage = inferP0DomainFromMessage; exports.cloneInvestigationState = cloneInvestigationState; exports.createEmptyInvestigationState = createEmptyInvestigationState; exports.updateInvestigationState = updateInvestigationState; const stage1Contracts_1 = require("../types/stage1Contracts"); const stage2ProblemUnits_1 = require("../types/stage2ProblemUnits"); function uniqueStrings(values) { return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))); } function capStrings(values, max) { return uniqueStrings(values).slice(0, max); } const INVESTIGATION_ACCOUNT_PREFIXES = new Set([ "01", "02", "07", "08", "10", "13", "19", "20", "21", "23", "25", "26", "28", "29", "41", "43", "44", "45", "50", "51", "52", "55", "57", "58", "60", "62", "66", "67", "68", "69", "70", "71", "73", "76", "90", "91", "94", "96", "97" ]); function collectDateLikeSpans(text) { const spans = []; const patterns = [ /\b20\d{2}(?:[-/.](?:0?[1-9]|1[0-2]))(?:[-/.](?:0?[1-9]|[12]\d|3[01]))?\b/g, /\b(?:0?[1-9]|[12]\d|3[01])[./-](?:0?[1-9]|1[0-2])[./-](?:\d{2}|\d{4})\b/g, /\b(?:0?[1-9]|[12]\d|3[01])\s+(?:январ[ьяе]|феврал[ьяе]|март[ае]?|апрел[ьяе]|ма[йея]|июн[ьяе]?|июл[ьяе]?|август[ае]?|сентябр[ьяе]?|октябр[ьяе]?|ноябр[ьяе]?|декабр[ьяе]?|january|february|march|april|may|june|july|august|september|october|november|december)(?:\s+20\d{2})?\b/giu ]; for (const pattern of patterns) { let match = null; while ((match = pattern.exec(text)) !== null) { spans.push({ start: match.index, end: match.index + match[0].length }); } } return spans; } function collectContractLikeSpans(text) { const spans = []; const patterns = [ /(?:\b(?:договор(?:а|у|ом|е)?|contract)\b[^\r\n]{0,24}(?:№|#|n|no\.?)\s*[a-zа-я0-9][a-zа-я0-9/_-]{1,})/giu, /(?:№|#)\s*[a-zа-я0-9_-]{1,10}\/[a-zа-я0-9_-]{1,12}/giu, /\b\d{2}\/\d{2}(?:-[a-zа-я]{1,10})?\b/giu ]; for (const pattern of patterns) { let match = null; while ((match = pattern.exec(text)) !== null) { spans.push({ start: match.index, end: match.index + match[0].length }); } } return spans; } function collectAmountLikeSpans(text) { const spans = []; const patterns = [/\b\d{1,3}(?:[ \u00A0]\d{3})+(?:[.,]\d{2})?\b/g, /\b\d+[.,]\d{2}\b/g]; for (const pattern of patterns) { let match = null; while ((match = pattern.exec(text)) !== null) { spans.push({ start: match.index, end: match.index + match[0].length }); } } return spans; } function collectPercentLikeSpans(text) { const spans = []; const pattern = /\b\d{1,3}(?:[.,]\d+)?\s*%/g; let match = null; while ((match = pattern.exec(text)) !== null) { spans.push({ start: match.index, end: match.index + match[0].length }); } return spans; } function intersectsSpan(start, end, spans) { return spans.some((span) => start < span.end && end > span.start); } function hasAccountContextAround(text, start, end) { const left = text.slice(Math.max(0, start - 32), start); const right = text.slice(end, Math.min(text.length, end + 32)); return /(?:счет|сч\.?|account|schet|оплат|расчет|расч[её]т|аванс|зачет|зач[её]т|ндс|закрыт|провод|постав|покуп|settlement|payment|vat|close|supplier|customer)/iu.test(`${left} ${right}`); } function detectAccounts(text) { const lower = String(text ?? "").toLowerCase(); const blockedSpans = [ ...collectDateLikeSpans(lower), ...collectAmountLikeSpans(lower), ...collectPercentLikeSpans(lower), ...collectContractLikeSpans(lower) ]; const hasAccountingLexeme = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b|оплат|расчет|расч[её]т|аванс|долг|settlement|payment|supplier|customer|ндс|vat|рбп|deferred|амортиз)/iu.test(lower); const accounts = new Set(); const contextualPattern = /(?:\b(?:счет(?:а|у|ом|ов)?|сч\.?|account(?:s)?|schet(?:a|u|om|ov)?)\b)\s*(?:№|#|:)?\s*(\d{2}(?:\.\d{1,2})?)/giu; let contextualMatch = null; while ((contextualMatch = contextualPattern.exec(lower)) !== null) { const token = String(contextualMatch[1] ?? "").trim(); const prefix = token.match(/^(\d{2})/)?.[1] ?? null; if (prefix && INVESTIGATION_ACCOUNT_PREFIXES.has(prefix)) { accounts.add(token); } } const pairPattern = /\b(\d{2}\.\d{1,2})\s*\/\s*(\d{2}\.\d{1,2})\b/g; let pairMatch = null; while ((pairMatch = pairPattern.exec(lower)) !== null) { const left = String(pairMatch[1] ?? "").trim(); const right = String(pairMatch[2] ?? "").trim(); const leftPrefix = left.match(/^(\d{2})/)?.[1] ?? null; const rightPrefix = right.match(/^(\d{2})/)?.[1] ?? null; if (leftPrefix && INVESTIGATION_ACCOUNT_PREFIXES.has(leftPrefix)) { accounts.add(left); } if (rightPrefix && INVESTIGATION_ACCOUNT_PREFIXES.has(rightPrefix)) { accounts.add(right); } } const genericPattern = /\b\d{2}(?:\.\d{1,2})?\b/g; let genericMatch = null; while ((genericMatch = genericPattern.exec(lower)) !== null) { const token = String(genericMatch[0] ?? "").trim(); const start = genericMatch.index; const end = start + token.length; if (intersectsSpan(start, end, blockedSpans)) { continue; } const prefix = token.match(/^(\d{2})/)?.[1] ?? null; if (!prefix || !INVESTIGATION_ACCOUNT_PREFIXES.has(prefix)) { continue; } if (!hasAccountingLexeme && !hasAccountContextAround(lower, start, end)) { continue; } accounts.add(token); } return capStrings(Array.from(accounts), stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS); } function detectPeriod(text) { const monthly = text.match(/\b(20\d{2})[-/.](0[1-9]|1[0-2])\b/); if (monthly) return `${monthly[1]}-${monthly[2]}`; const yearly = text.match(/\b(20\d{2})\b/); if (yearly) return yearly[1]; return null; } function inferP0DomainFromMessage(text) { const messageCorpus = String(text ?? "").toLowerCase(); const accounts = detectAccounts(text); const hasSettlementAccount = accounts.some((item) => isSettlementAccount(item)); const hasVatAccount = accounts.some((item) => isVatAccount(item)); const hasCloseAccount = accounts.some((item) => isCloseCostsAccount(item)); const hasFixedAssetAccount = accounts.some((item) => isFixedAssetAccount(item)); const hasSettlementLexical = /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(messageCorpus); const hasVatLexical = /(?:ндс|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(messageCorpus); const hasCloseLexical = /(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost|рбп)/i.test(messageCorpus); const hasExplicitFixedAssetLexical = /(?:амортиз|основн(ые|ых|ым)?\s+средств|объект[а-яё]*\s+ос|fixed\s*asset|depreciat|сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|account\s*0[128])/i.test(messageCorpus); const hasBroadMonthCloseLexical = /(?:после\s+закрытия|косвенн|период(?:а)?\s+закрыт|month\s*close|period\s*close|регламентн)/i.test(messageCorpus); // Keep settlement lane stable when 60/62 lexical/account anchors are explicit // and there is no explicit VAT intent. if ((hasSettlementAccount || hasSettlementLexical) && !hasVatLexical && !hasVatAccount && !hasExplicitFixedAssetLexical) { return "settlements_60_62"; } if ((hasCloseAccount || hasCloseLexical || hasBroadMonthCloseLexical) && !hasVatLexical && !hasVatAccount && !hasExplicitFixedAssetLexical && !hasFixedAssetAccount) { return "month_close_costs_20_44"; } if (hasVatAccount || hasVatLexical) { return "vat_document_register_book"; } if (hasFixedAssetAccount || hasExplicitFixedAssetLexical) { return "fixed_asset_amortization"; } if (hasCloseAccount || hasCloseLexical) { return "month_close_costs_20_44"; } if (hasSettlementAccount || hasSettlementLexical) { return "settlements_60_62"; } return null; } function buildQuestionScopeId(input) { const domainPart = String(input.domain ?? "").trim(); const periodPart = String(input.period ?? "").trim(); const accountPart = capStrings(input.accounts.map((item) => String(item ?? "").trim()).filter(Boolean), 4).join(","); const subjectPart = String(input.subject ?? "").trim().slice(0, 96).toLowerCase(); const parts = [ domainPart ? `d:${domainPart}` : "", periodPart ? `p:${periodPart}` : "", accountPart ? `a:${accountPart}` : "", subjectPart ? `s:${subjectPart}` : "" ].filter(Boolean); if (parts.length === 0) { return null; } return parts.join("|"); } function deriveScopeOrigin(input) { if (input.followupApplied) { return "followup_state_carryover"; } const hasExplicitPeriod = Boolean(detectPeriod(input.userMessage)); const hasExplicitAccounts = detectAccounts(input.userMessage).length > 0; const explicitDomain = inferP0DomainFromMessage(input.userMessage); if (hasExplicitPeriod || hasExplicitAccounts || explicitDomain) { return "explicit_from_message"; } const routeDomain = deriveDomain(input.routeSummary); if (routeDomain && routeDomain !== "no_route") { return "route_derived"; } return "underspecified"; } function deriveDomain(routeSummary) { if (!routeSummary) return null; if (routeSummary.mode === "legacy_v1") { return routeSummary.route_hint; } const routes = routeSummary.decisions.map((item) => item.route).filter((route) => route !== "no_route"); const uniqueRoutes = uniqueStrings(routes); if (uniqueRoutes.length === 0) { return "no_route"; } return uniqueRoutes.join(","); } function deriveNarrowingStatus(routeSummary, coverageReport) { if (!routeSummary) { return "unknown"; } if (routeSummary.mode === "legacy_v1") { return "not_needed"; } if (routeSummary.fallback.type === "clarification" || coverageReport.clarification_needed_for.length > 0) { return "needs_clarification"; } const hasNoRoute = routeSummary.decisions.some((item) => item.route === "no_route"); if (hasNoRoute) { return "broad_guarded"; } return routeSummary.decisions.length > 1 ? "applied" : "not_needed"; } function deriveQueryModeHint(routeSummary) { if (!routeSummary) { return "investigation_candidate"; } if (routeSummary.mode === "legacy_v1") { return "direct_answer"; } return routeSummary.fallback.type === "none" ? "direct_answer" : "investigation_candidate"; } function collectEvidenceRefs(retrievalResults) { const refs = retrievalResults.flatMap((result) => result.evidence.map((item) => item.evidence_id)); return capStrings(refs, stage1Contracts_1.INVESTIGATION_MAX_EVIDENCE_REFS); } function collectOpenUncertainties(coverageReport, retrievalResults) { const requirementNotes = [ ...coverageReport.requirements_uncovered.map((item) => `uncovered:${item}`), ...coverageReport.requirements_partially_covered.map((item) => `partial:${item}`), ...coverageReport.clarification_needed_for.map((item) => `clarify:${item}`), ...coverageReport.out_of_scope_requirements.map((item) => `out_of_scope:${item}`) ]; const limitationNotes = retrievalResults.flatMap((result) => result.limitations).slice(0, 6); return capStrings([...requirementNotes, ...limitationNotes], stage1Contracts_1.INVESTIGATION_MAX_UNCERTAINTIES); } function normalizeAccountPrefix(value) { const account = String(value ?? "").trim(); if (!account) { return null; } const match = account.match(/^(\d{2})/); return match?.[1] ?? null; } function isSettlementAccount(value) { const prefix = normalizeAccountPrefix(value); return prefix === "60" || prefix === "62" || prefix === "51" || prefix === "76"; } function isVatAccount(value) { const prefix = normalizeAccountPrefix(value); return prefix === "19" || prefix === "68"; } function isFixedAssetAccount(value) { const prefix = normalizeAccountPrefix(value); return prefix === "01" || prefix === "02" || prefix === "08"; } function isCloseCostsAccount(value) { const prefix = normalizeAccountPrefix(value); if (!prefix) { return false; } const account = Number(prefix); return (account >= 20 && account <= 44) || prefix === "97"; } function inferFollowupActiveDomain(input) { const messageCorpus = String(input.userMessage ?? "").toLowerCase(); const contextualCorpus = input.allowStateCarryover ? `${messageCorpus} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase() : messageCorpus; const hasFixedAssetLexicalSignal = /(?:амортиз|основн(ые|ых|ым)?\s+средств|объект[а-яё]*\s+ос|fixed\s*asset|depreciat|сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|account\s*0[128])/i.test(messageCorpus); const hasFixedAssetAccountSignal = input.focusAccounts.some((item) => isFixedAssetAccount(item)) && /(?:сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|(?:01|02|08)(?:\.\d{2})?\s*\/\s*(?:01|02|08)(?:\.\d{2})?|\b0[128](?:\.\d{2})?\b)/i.test(messageCorpus); const hasBroadMonthCloseSignal = /(?:после\s+закрытия|косвенн|период(?:а)?\s+закрыт|регламентн|month\s*close|period\s*close)/i.test(messageCorpus); if ((input.focusAccounts.some((item) => isCloseCostsAccount(item)) || /(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(messageCorpus) || hasBroadMonthCloseSignal) && !hasFixedAssetLexicalSignal && !hasFixedAssetAccountSignal) { return "month_close_costs_20_44"; } if (hasFixedAssetLexicalSignal || hasFixedAssetAccountSignal) { return "fixed_asset_amortization"; } const hasSettlementSignal = input.focusAccounts.some((item) => isSettlementAccount(item)) || /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(messageCorpus); if (hasSettlementSignal) { return "settlements_60_62"; } const hasVatSignal = input.focusAccounts.some((item) => isVatAccount(item)) || /(?:ндс|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(messageCorpus); if (hasVatSignal) { return "vat_document_register_book"; } const hasCloseSignal = input.focusAccounts.some((item) => isCloseCostsAccount(item)) || /(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(messageCorpus); if (hasCloseSignal) { return "month_close_costs_20_44"; } if (input.allowStateCarryover && /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|аванс|долг|settlement|payment)/i.test(contextualCorpus) && (input.previous.followup_context?.active_domain === "settlements_60_62" || input.previous.focus.domain === "settlements_60_62")) { return "settlements_60_62"; } const routeDomain = deriveDomain(input.routeSummary); if (routeDomain && routeDomain !== "no_route") { return routeDomain; } if (input.allowStateCarryover) { return input.previous.followup_context?.active_domain ?? input.previous.focus.domain ?? null; } return null; } function collectUncoveredRequirementIds(coverageReport) { return capStrings([ ...coverageReport.requirements_uncovered, ...coverageReport.requirements_partially_covered, ...coverageReport.clarification_needed_for, ...coverageReport.out_of_scope_requirements ], stage1Contracts_1.INVESTIGATION_MAX_REQUIREMENT_LINKS); } function collectEvidenceSummary(retrievalResults) { const lines = retrievalResults.map((result) => { const requirementRef = result.requirement_ids[0] ?? result.fragment_id; return `${requirementRef}:${result.status}:${result.route}`; }); return capStrings(lines, 6); } function settlementFocusActions(activeDomain) { if (activeDomain !== "settlements_60_62") { return []; } return [ "Проверьте договор и объект расчетов по платежу.", "Сверьте регистр расчетов и привязку платежа к закрывающему документу.", "Проверьте зачет аванса или взаимозачет по связке 60/62." ]; } function normalizeEntityBacklinks(values) { const result = []; const seen = new Set(); for (const item of values) { const entity = String(item.entity ?? "").trim(); const id = String(item.id ?? "").trim(); if (!entity || !id) { continue; } const key = `${entity}::${id}`; if (seen.has(key)) { continue; } seen.add(key); result.push({ entity, id }); } return result; } function collectProblemUnits(retrievalResults) { return retrievalResults.flatMap((result) => result.problem_units ?? []); } function capProblemUnitState(state) { return { active_problem_units: capStrings(state.active_problem_units, stage2ProblemUnits_1.INVESTIGATION_MAX_ACTIVE_PROBLEM_UNITS), resolved_problem_units: capStrings(state.resolved_problem_units, stage2ProblemUnits_1.INVESTIGATION_MAX_RESOLVED_PROBLEM_UNITS), problem_unit_backlinks: state.problem_unit_backlinks .map((item) => ({ problem_unit_id: String(item.problem_unit_id ?? "").trim(), entity_backlinks: normalizeEntityBacklinks(item.entity_backlinks ?? []) })) .filter((item) => Boolean(item.problem_unit_id) && item.entity_backlinks.length > 0) .slice(0, stage2ProblemUnits_1.INVESTIGATION_MAX_PROBLEM_UNIT_BACKLINKS), focus_problem_types: capStrings(state.focus_problem_types.map((item) => String(item)), stage2ProblemUnits_1.INVESTIGATION_MAX_FOCUS_PROBLEM_TYPES) }; } function updateProblemUnitState(previous, retrievalResults) { const previousState = previous.problem_unit_state; const currentProblemUnits = collectProblemUnits(retrievalResults); const currentIds = capStrings(currentProblemUnits.map((item) => String(item.problem_unit_id ?? "")), stage2ProblemUnits_1.INVESTIGATION_MAX_ACTIVE_PROBLEM_UNITS); const currentTypes = capStrings(currentProblemUnits.map((item) => String(item.problem_unit_type ?? "")), stage2ProblemUnits_1.INVESTIGATION_MAX_FOCUS_PROBLEM_TYPES); const currentBacklinksRaw = currentProblemUnits .filter((item) => currentIds.includes(item.problem_unit_id)) .map((item) => ({ problem_unit_id: item.problem_unit_id, entity_backlinks: normalizeEntityBacklinks(item.entity_backlinks ?? []) })) .filter((item) => item.entity_backlinks.length > 0); const currentBacklinksById = new Map(currentBacklinksRaw.map((item) => [item.problem_unit_id, item.entity_backlinks])); const previousBacklinksById = new Map((previousState?.problem_unit_backlinks ?? []).map((item) => [item.problem_unit_id, item.entity_backlinks])); const active_problem_units = currentIds.length > 0 ? currentIds : capStrings(previousState?.active_problem_units ?? [], stage2ProblemUnits_1.INVESTIGATION_MAX_ACTIVE_PROBLEM_UNITS); const resolved_problem_units = currentIds.length > 0 ? capStrings([ ...(previousState?.active_problem_units ?? []).filter((item) => !currentIds.includes(item)), ...(previousState?.resolved_problem_units ?? []) ], stage2ProblemUnits_1.INVESTIGATION_MAX_RESOLVED_PROBLEM_UNITS) : capStrings(previousState?.resolved_problem_units ?? [], stage2ProblemUnits_1.INVESTIGATION_MAX_RESOLVED_PROBLEM_UNITS); const problem_unit_backlinks = active_problem_units .map((problemUnitId) => { const entity_backlinks = normalizeEntityBacklinks(currentBacklinksById.get(problemUnitId) ?? previousBacklinksById.get(problemUnitId) ?? []); if (entity_backlinks.length === 0) { return null; } return { problem_unit_id: problemUnitId, entity_backlinks }; }) .filter((item) => item !== null) .slice(0, stage2ProblemUnits_1.INVESTIGATION_MAX_PROBLEM_UNIT_BACKLINKS); const focus_problem_types = currentTypes.length > 0 ? currentTypes : capStrings((previousState?.focus_problem_types ?? []).map((item) => String(item)), stage2ProblemUnits_1.INVESTIGATION_MAX_FOCUS_PROBLEM_TYPES); const nextState = capProblemUnitState({ active_problem_units, resolved_problem_units, problem_unit_backlinks, focus_problem_types }); if (nextState.active_problem_units.length === 0 && nextState.resolved_problem_units.length === 0 && nextState.problem_unit_backlinks.length === 0 && nextState.focus_problem_types.length === 0) { return undefined; } return nextState; } function cloneInvestigationState(state) { if (!state) return null; const cloned = { ...state, focus: { ...state.focus, primary_accounts: [...state.focus.primary_accounts] }, evidence_refs: [...state.evidence_refs], open_uncertainties: [...state.open_uncertainties], followup_context: state.followup_context ? { ...state.followup_context, referenced_requirement_ids: [...state.followup_context.referenced_requirement_ids], ...(state.followup_context.active_requirement_ids ? { active_requirement_ids: [...state.followup_context.active_requirement_ids] } : {}), ...(state.followup_context.uncovered_requirement_ids ? { uncovered_requirement_ids: [...state.followup_context.uncovered_requirement_ids] } : {}), ...(state.followup_context.settlement_next_actions ? { settlement_next_actions: [...state.followup_context.settlement_next_actions] } : {}), ...(state.followup_context.evidence_summary ? { evidence_summary: [...state.followup_context.evidence_summary] } : {}) } : null }; if (state.problem_unit_state) { cloned.problem_unit_state = capProblemUnitState({ active_problem_units: [...state.problem_unit_state.active_problem_units], resolved_problem_units: [...state.problem_unit_state.resolved_problem_units], problem_unit_backlinks: state.problem_unit_state.problem_unit_backlinks.map((item) => ({ problem_unit_id: item.problem_unit_id, entity_backlinks: [...item.entity_backlinks] })), focus_problem_types: [...state.problem_unit_state.focus_problem_types] }); } return cloned; } function createEmptyInvestigationState(sessionId, timestamp = new Date().toISOString()) { return { schema_version: stage1Contracts_1.INVESTIGATION_STATE_SCHEMA_VERSION, session_id: sessionId, status: "idle", turn_index: 0, updated_at: timestamp, question_id: null, question_scope_id: null, scope_origin: null, focus: { domain: null, period: null, primary_accounts: [], active_query_subject: null }, narrowing_status: "unknown", evidence_refs: [], open_uncertainties: [], last_answer_mode: null, followup_context: null, query_mode_hint: "direct_answer" }; } function updateInvestigationState(input) { const previous = input.previous; const followupApplied = input.followupApplied === true; const focusFromMessage = capStrings(detectAccounts(input.userMessage), stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS); const mergedFocusAccounts = followupApplied ? capStrings([...focusFromMessage, ...previous.focus.primary_accounts], stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS) : capStrings(focusFromMessage, stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS); const requirementIds = capStrings(input.requirements.map((item) => item.requirement_id), stage1Contracts_1.INVESTIGATION_MAX_REQUIREMENT_LINKS); const mainRequirement = input.requirements[0]?.requirement_text ?? input.userMessage; const problemUnitState = updateProblemUnitState(previous, input.retrievalResults); const uncoveredRequirementIds = collectUncoveredRequirementIds(input.coverageReport); const routeDomain = deriveDomain(input.routeSummary); const activeDomain = inferFollowupActiveDomain({ userMessage: input.userMessage, focusAccounts: focusFromMessage, routeSummary: input.routeSummary, previous, allowStateCarryover: followupApplied }); const focusDomain = activeDomain ?? routeDomain ?? (followupApplied ? previous.focus.domain : null); const detectedPeriod = detectPeriod(input.userMessage); const focusPeriod = detectedPeriod ?? (followupApplied ? previous.focus.period : null); const settlementNextActions = settlementFocusActions(activeDomain); const lastProblemUnitId = problemUnitState?.active_problem_units[0] ?? null; const evidenceSummary = collectEvidenceSummary(input.retrievalResults); const scopeOrigin = deriveScopeOrigin({ followupApplied, userMessage: input.userMessage, routeSummary: input.routeSummary }); const questionScopeId = buildQuestionScopeId({ domain: focusDomain, period: focusPeriod, accounts: mergedFocusAccounts, subject: mainRequirement }); return { schema_version: stage1Contracts_1.INVESTIGATION_STATE_SCHEMA_VERSION, session_id: previous.session_id, status: "active", turn_index: previous.turn_index + 1, updated_at: input.timestamp, question_id: input.questionId, question_scope_id: questionScopeId, scope_origin: scopeOrigin, focus: { domain: focusDomain, period: focusPeriod, primary_accounts: mergedFocusAccounts, active_query_subject: mainRequirement.slice(0, 180) }, narrowing_status: deriveNarrowingStatus(input.routeSummary, input.coverageReport), evidence_refs: capStrings([...collectEvidenceRefs(input.retrievalResults), ...previous.evidence_refs], stage1Contracts_1.INVESTIGATION_MAX_EVIDENCE_REFS), open_uncertainties: collectOpenUncertainties(input.coverageReport, input.retrievalResults), last_answer_mode: input.replyType, followup_context: { previous_question_id: previous.question_id, last_user_message: input.userMessage.slice(0, 240), referenced_requirement_ids: requirementIds, active_domain: activeDomain, active_requirement_ids: requirementIds, uncovered_requirement_ids: uncoveredRequirementIds, last_problem_unit_id: lastProblemUnitId, settlement_next_actions: settlementNextActions, evidence_summary: evidenceSummary, question_scope_id: questionScopeId, scope_origin: scopeOrigin }, query_mode_hint: deriveQueryModeHint(input.routeSummary), ...(problemUnitState ? { problem_unit_state: problemUnitState } : {}) }; }