import { buildAddressMemoryRecapReply as buildAddressMemoryRecapReplyFromPolicy, buildBroadBusinessEvaluationReply as buildBroadBusinessEvaluationReplyFromPolicy, buildConversationExecutiveSummaryReply as buildConversationExecutiveSummaryReplyFromPolicy, buildSelectedObjectAnswerInspectionReply as buildSelectedObjectAnswerInspectionReplyFromPolicy, buildInventoryHistoryCapabilityFollowupReply as buildInventoryHistoryCapabilityFollowupReplyFromPolicy, resolveAssistantLivingChatMemoryContext } from "./assistantMemoryRecapPolicy"; import { resolveAssistantOrganizationAuthority } from "./assistantContinuityPolicy"; import { attachAssistantMcpDiscoveryDebug } from "./assistantMcpDiscoveryDebugAttachment"; import { applyAssistantMcpDiscoveryResponsePolicy } from "./assistantMcpDiscoveryResponsePolicy"; export interface AssistantLivingChatSessionScopeInput { knownOrganizations?: unknown[]; selectedOrganization?: unknown; activeOrganization?: unknown; } export interface AssistantLivingChatModeDecisionInput { mode?: string | null; reason?: string | null; } export interface AssistantLivingChatRuntimeInput { userMessage: string; sessionItems: unknown[]; modeDecision?: AssistantLivingChatModeDecisionInput | null; sessionScope: AssistantLivingChatSessionScopeInput; addressRuntimeMeta?: Record | null; traceIdFactory: () => string; toNonEmptyString: (value: unknown) => string | null; mergeKnownOrganizations: (values: unknown[]) => string[]; hasAssistantDataScopeMetaQuestionSignal: (message: string) => boolean; shouldHandleAsAssistantCapabilityMetaQuery: (message: string) => boolean; hasDestructiveDataActionSignal: (message: string) => boolean; hasDangerOrCoercionSignal: (message: string) => boolean; hasOperationalAdminActionRequestSignal: (message: string) => boolean; hasOrganizationFactLookupSignal: (message: string) => boolean; hasOrganizationFactFollowupSignal: (message: string, items: unknown[]) => boolean; hasLivingChatSignal: (message: string) => boolean; shouldEmitOrganizationSelectionReply: (message: string, activeOrganization: string | null) => boolean; hasAssistantCapabilityQuestionSignal: (message: string) => boolean; resolveDataScopeProbe: () => Promise | null>; executeLlmChat: () => Promise; applyScriptGuard: (chatText: string, userMessage: string) => { text: string; applied: boolean; reason: string | null; }; applyGroundingGuard: (input: { userMessage: string; chatText: string; organization: string | null; }) => { text: string; applied: boolean; reason: string | null; }; buildAssistantSafetyRefusalReply: () => string; buildAssistantDataScopeContractReply: (scopeProbe: Record | null) => string; buildAssistantProactiveOrganizationOfferReply: (scopeProbe: Record | null) => string; buildAssistantOrganizationFactBoundaryReply: (organization: string | null) => string; buildAssistantDataScopeSelectionReply: (organization: string | null) => string; buildAssistantOperationalBoundaryReply: () => string; buildAssistantCapabilityContractReply: (userMessage?: string) => string; } export interface AssistantLivingChatRuntimeOutput { handled: boolean; chatText: string; debug: Record | null; } function hasPriorAssistantTurn(items: unknown[]): boolean { if (!Array.isArray(items)) { return false; } return items.some((item) => item && typeof item === "object" && (item as { role?: string }).role === "assistant"); } function buildDeterministicSmalltalkLeadReply(): string { return "\u041f\u0440\u0438\u0432\u0435\u0442! \u0412\u0441\u0451 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e."; } function hasConversationExecutiveSummarySignal(value: unknown): boolean { const normalized = String(value ?? "") .toLowerCase() .replace(/\u0451/gu, "\u0435") .replace(/\s+/g, " ") .trim(); return /(?:executive\s+summary|финальн\w*\s+собери|итогов\w*\s+(?:резюм|summary|вывод)|по\s+всему\s+диалогу|где\s+ответы\s+были\s+подтвержден|где\s+proxy|где\s+прокси|не\s+хватил\w*\s+доказательств|ручн\w*\s+(?:смотр|провер|контрол))/iu.test( normalized ); } function asRecord(value: unknown): Record | null { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : null; } function firstMeaningEntityLabel(assistantTurnMeaning: Record | null): string | null { const candidates = Array.isArray(assistantTurnMeaning?.explicit_entity_candidates) ? assistantTurnMeaning?.explicit_entity_candidates : []; for (const candidate of candidates) { const record = asRecord(candidate); const value = typeof record?.value === "string" ? record.value.trim() : ""; if (value.length > 0) { return value; } } return null; } function buildUnsupportedCurrentTurnMeaningBoundaryReply(input: { assistantTurnMeaning: Record | null; }): string { const family = typeof input.assistantTurnMeaning?.unsupported_but_understood_family === "string" ? input.assistantTurnMeaning.unsupported_but_understood_family : null; const entityLabel = firstMeaningEntityLabel(input.assistantTurnMeaning); if (family === "counterparty_value_or_turnover") { const entityPart = entityLabel ? ` \u043f\u043e \u00ab${entityLabel}\u00bb` : ""; return [ `\u042f \u043f\u043e\u043d\u044f\u043b \u0432\u043e\u043f\u0440\u043e\u0441: \u043d\u0443\u0436\u0435\u043d \u043e\u0431\u043e\u0440\u043e\u0442${entityPart}.`, "\u0422\u043e\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u0430\u0441\u0447\u0451\u0442\u0430 \u0435\u0449\u0451 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0448\u043b\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u043b\u0438 \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430.", "\u041c\u043e\u0433\u0443 \u043f\u043e\u043a\u0430 \u043d\u0430\u0434\u0451\u0436\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u044b \u0438\u043b\u0438 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443." ].join(" "); } return "\u042f \u043f\u043e\u043d\u044f\u043b \u0441\u043c\u044b\u0441\u043b \u043d\u043e\u0432\u043e\u0433\u043e \u0432\u043e\u043f\u0440\u043e\u0441\u0430, \u043d\u043e \u0442\u043e\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0435\u0449\u0451 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d. \u041d\u0435 \u0431\u0443\u0434\u0443 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u044d\u0442\u043e \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435."; } export async function runAssistantLivingChatRuntime( input: AssistantLivingChatRuntimeInput ): Promise { const userMessage = String(input.userMessage ?? ""); const organizationAuthority = resolveAssistantOrganizationAuthority({ sessionItems: input.sessionItems, sessionKnownOrganizations: Array.isArray(input.sessionScope.knownOrganizations) ? input.sessionScope.knownOrganizations : [], sessionSelectedOrganization: input.sessionScope.selectedOrganization, sessionActiveOrganization: input.sessionScope.activeOrganization, toNonEmptyString: input.toNonEmptyString, normalizeOrganizationScopeValue: input.toNonEmptyString, mergeKnownOrganizations: input.mergeKnownOrganizations }); const continuitySnapshot = organizationAuthority.continuitySnapshot; const dataScopeMetaQuery = input.hasAssistantDataScopeMetaQuestionSignal(userMessage); const capabilityMetaQuery = input.shouldHandleAsAssistantCapabilityMetaQuery(userMessage); const destructiveSignal = input.hasDestructiveDataActionSignal(userMessage); const dangerSignal = input.hasDangerOrCoercionSignal(userMessage); const operationalSignal = input.hasOperationalAdminActionRequestSignal(userMessage); let dataScopeProbe: Record | null = null; let chatText = ""; let livingChatSource = "llm_chat"; let livingChatScriptGuardApplied = false; let livingChatScriptGuardReason: string | null = null; let livingChatGroundingGuardApplied = false; let livingChatGroundingGuardReason: string | null = null; let livingChatProactiveScopeOfferApplied = false; const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization; let knownOrganizations = [...organizationAuthority.knownOrganizations]; let selectedOrganization = organizationAuthority.selectedOrganization; let activeOrganization = organizationAuthority.activeOrganization; const addressRuntimeMeta = (input.addressRuntimeMeta && typeof input.addressRuntimeMeta === "object" ? input.addressRuntimeMeta : {}) as Record; const orchestrationContract = asRecord(addressRuntimeMeta.orchestrationContract); const assistantTurnMeaning = asRecord(orchestrationContract?.assistant_turn_meaning); const unsupportedCurrentTurnMeaningBoundary = Boolean( input.modeDecision?.reason === "unsupported_current_turn_meaning_boundary" || orchestrationContract?.unsupported_current_turn_meaning_boundary === true ); const memoryRecapContext = resolveAssistantLivingChatMemoryContext({ modeDecisionReason: input.modeDecision?.reason ?? null, sessionItems: input.sessionItems }); const contextualInventoryHistoryCapabilityFollowup = memoryRecapContext.contextualInventoryHistoryCapabilityFollowup; const contextualMemoryRecapFollowup = memoryRecapContext.contextualMemoryRecapFollowup; const contextualAnswerInspectionFollowup = memoryRecapContext.contextualAnswerInspectionFollowup; const lastGroundedInventoryAddressDebug = memoryRecapContext.lastGroundedInventoryAddressDebug; const lastMemoryAddressDebug = memoryRecapContext.lastMemoryAddressDebug; const lastAnswerInspectionAddressDebug = memoryRecapContext.lastAnswerInspectionAddressDebug; if (capabilityMetaQuery && (destructiveSignal || dangerSignal)) { chatText = input.buildAssistantSafetyRefusalReply(); livingChatSource = "deterministic_safety_refusal"; } else if (dataScopeMetaQuery) { dataScopeProbe = await input.resolveDataScopeProbe(); chatText = input.buildAssistantDataScopeContractReply(dataScopeProbe); knownOrganizations = input.mergeKnownOrganizations([ ...knownOrganizations, ...(Array.isArray(dataScopeProbe?.organizations) ? (dataScopeProbe.organizations as unknown[]) : []) ]); if (!activeOrganization && knownOrganizations.length === 1) { activeOrganization = knownOrganizations[0]; } livingChatSource = dataScopeProbe?.status === "resolved" ? "deterministic_data_scope_contract_live" : "deterministic_data_scope_contract"; } else if (unsupportedCurrentTurnMeaningBoundary) { const unsupportedFamily = typeof assistantTurnMeaning?.unsupported_but_understood_family === "string" ? assistantTurnMeaning.unsupported_but_understood_family : null; if (unsupportedFamily === "broad_business_evaluation") { const scopedOrganization = selectedOrganization ?? activeOrganization ?? continuityActiveOrganization ?? null; chatText = buildBroadBusinessEvaluationReplyFromPolicy({ organization: scopedOrganization, addressDebug: continuitySnapshot.lastGroundedAddressDebug, sessionItems: input.sessionItems, toNonEmptyString: input.toNonEmptyString }); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = "deterministic_broad_business_evaluation_contract"; } else { chatText = buildUnsupportedCurrentTurnMeaningBoundaryReply({ assistantTurnMeaning }); livingChatSource = "deterministic_unsupported_current_turn_boundary"; } } else if ((selectedOrganization || activeOrganization) && input.hasOrganizationFactLookupSignal(userMessage)) { const scopedOrganization = selectedOrganization ?? activeOrganization ?? null; chatText = input.buildAssistantOrganizationFactBoundaryReply(scopedOrganization); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = "deterministic_organization_fact_boundary"; } else if ( (selectedOrganization || activeOrganization) && input.hasOrganizationFactFollowupSignal(userMessage, input.sessionItems) ) { const scopedOrganization = selectedOrganization ?? activeOrganization ?? null; chatText = input.buildAssistantOrganizationFactBoundaryReply(scopedOrganization); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = "deterministic_organization_fact_boundary_followup"; } else if ( !capabilityMetaQuery && input.shouldEmitOrganizationSelectionReply(userMessage, selectedOrganization ?? activeOrganization) ) { const scopedOrganization = selectedOrganization ?? activeOrganization ?? null; chatText = input.buildAssistantDataScopeSelectionReply(scopedOrganization); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = "deterministic_data_scope_selection_contract"; } else if (capabilityMetaQuery && operationalSignal && !input.hasAssistantCapabilityQuestionSignal(userMessage)) { chatText = input.buildAssistantOperationalBoundaryReply(); livingChatSource = "deterministic_operational_boundary"; } else if (contextualInventoryHistoryCapabilityFollowup) { const scopedOrganization = selectedOrganization ?? activeOrganization ?? null; chatText = buildInventoryHistoryCapabilityFollowupReplyFromPolicy({ organization: scopedOrganization, addressDebug: lastGroundedInventoryAddressDebug, toNonEmptyString: input.toNonEmptyString }); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = "deterministic_inventory_history_capability_contract"; } else if (contextualMemoryRecapFollowup) { const scopedOrganization = selectedOrganization ?? activeOrganization ?? continuityActiveOrganization ?? null; const executiveSummaryFollowup = hasConversationExecutiveSummarySignal(userMessage); chatText = executiveSummaryFollowup ? buildConversationExecutiveSummaryReplyFromPolicy({ organization: scopedOrganization, addressDebug: lastMemoryAddressDebug, sessionItems: input.sessionItems, toNonEmptyString: input.toNonEmptyString }) : buildAddressMemoryRecapReplyFromPolicy({ organization: scopedOrganization, addressDebug: lastMemoryAddressDebug, sessionItems: input.sessionItems, toNonEmptyString: input.toNonEmptyString }); activeOrganization = scopedOrganization ?? activeOrganization; livingChatSource = executiveSummaryFollowup ? "deterministic_conversation_executive_summary_contract" : "deterministic_memory_recap_contract"; } else if (contextualAnswerInspectionFollowup) { chatText = buildSelectedObjectAnswerInspectionReplyFromPolicy({ addressDebug: lastAnswerInspectionAddressDebug, toNonEmptyString: input.toNonEmptyString }); livingChatSource = "deterministic_answer_inspection_contract"; } else if (capabilityMetaQuery) { chatText = input.buildAssistantCapabilityContractReply(userMessage); livingChatSource = "deterministic_capability_contract"; } else { chatText = await input.executeLlmChat(); const scriptGuard = input.applyScriptGuard(chatText, userMessage); chatText = scriptGuard.text; if (scriptGuard.applied) { livingChatScriptGuardApplied = true; livingChatScriptGuardReason = scriptGuard.reason; livingChatSource = "llm_chat_script_guard"; } const groundingGuard = input.applyGroundingGuard({ userMessage, chatText, organization: activeOrganization ?? selectedOrganization ?? null }); chatText = groundingGuard.text; if (groundingGuard.applied) { livingChatGroundingGuardApplied = true; livingChatGroundingGuardReason = groundingGuard.reason; livingChatSource = "llm_chat_grounding_guard"; } const shouldOfferProactiveOrganizationScope = !selectedOrganization && !activeOrganization && !continuitySnapshot.hasGroundedAddressContext && !hasPriorAssistantTurn(input.sessionItems) && input.modeDecision?.mode === "chat" && input.hasLivingChatSignal(userMessage); if (shouldOfferProactiveOrganizationScope) { const proactiveScopeProbe = await input.resolveDataScopeProbe(); const mergedKnownOrganizations = input.mergeKnownOrganizations([ ...knownOrganizations, ...(Array.isArray(proactiveScopeProbe?.organizations) ? (proactiveScopeProbe.organizations as unknown[]) : []) ]); knownOrganizations = mergedKnownOrganizations; if (!activeOrganization && mergedKnownOrganizations.length === 1) { activeOrganization = mergedKnownOrganizations[0]; } const proactiveOffer = input.buildAssistantProactiveOrganizationOfferReply(proactiveScopeProbe); if (proactiveOffer) { chatText = [buildDeterministicSmalltalkLeadReply(), proactiveOffer] .filter((part) => String(part ?? "").trim().length > 0) .join(" "); livingChatProactiveScopeOfferApplied = true; livingChatSource = "deterministic_smalltalk_with_proactive_scope_offer"; if (!dataScopeProbe) { dataScopeProbe = proactiveScopeProbe; } } } } if (!chatText) { return { handled: false, chatText: "", debug: null }; } const mcpDiscoveryResponsePolicy = applyAssistantMcpDiscoveryResponsePolicy({ currentReply: chatText, currentReplySource: livingChatSource, livingChatSource, modeDecisionReason: input.modeDecision?.reason ?? null, addressRuntimeMeta }); if (mcpDiscoveryResponsePolicy.applied) { chatText = mcpDiscoveryResponsePolicy.reply_text; livingChatSource = mcpDiscoveryResponsePolicy.reply_source; } const predecomposeContract = addressRuntimeMeta.predecomposeContract && typeof addressRuntimeMeta.predecomposeContract === "object" ? (addressRuntimeMeta.predecomposeContract as Record) : null; const semanticExtractionContract = addressRuntimeMeta.semanticExtractionContract && typeof addressRuntimeMeta.semanticExtractionContract === "object" ? (addressRuntimeMeta.semanticExtractionContract as Record) : null; const debug: Record = { trace_id: input.traceIdFactory(), prompt_version: "living_chat_router_v1", schema_version: "living_chat_router_v1", fallback_type: "none", detected_mode: "chat", detected_mode_confidence: "high", execution_lane: "living_chat", living_router_mode: input.modeDecision?.mode ?? "chat", living_router_reason: input.modeDecision?.reason ?? "living_chat_signal_detected", living_chat_response_source: livingChatSource, mcp_discovery_response_policy_v1: mcpDiscoveryResponsePolicy, mcp_discovery_response_candidate_v1: mcpDiscoveryResponsePolicy.candidate, mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied, living_chat_script_guard_applied: livingChatScriptGuardApplied, living_chat_script_guard_reason: livingChatScriptGuardReason, living_chat_grounding_guard_applied: livingChatGroundingGuardApplied, living_chat_grounding_guard_reason: livingChatGroundingGuardReason, living_chat_proactive_scope_offer_applied: livingChatProactiveScopeOfferApplied, living_chat_data_scope_probe_status: dataScopeProbe?.status ?? null, living_chat_data_scope_probe_channel: dataScopeProbe?.channel ?? null, living_chat_data_scope_probe_org_count: Array.isArray(dataScopeProbe?.organizations) ? dataScopeProbe.organizations.length : 0, living_chat_data_scope_probe_organizations: Array.isArray(dataScopeProbe?.organizations) ? input.mergeKnownOrganizations(dataScopeProbe.organizations as unknown[]) : [], living_chat_data_scope_probe_error: dataScopeProbe?.error ?? null, living_chat_continuity_grounded_context_detected: continuitySnapshot.hasGroundedAddressContext, living_chat_continuity_active_organization: continuityActiveOrganization, living_chat_selected_organization: selectedOrganization ?? null, assistant_known_organizations: knownOrganizations, assistant_active_organization: activeOrganization ?? null, address_llm_predecompose_attempted: Boolean(addressRuntimeMeta.attempted), address_llm_predecompose_applied: Boolean(addressRuntimeMeta.applied), address_llm_predecompose_reason: addressRuntimeMeta.reason ?? null, address_llm_predecompose_contract: predecomposeContract, address_semantic_extraction_contract: semanticExtractionContract, orchestration_contract_v1: addressRuntimeMeta.orchestrationContract ?? null, tool_gate_decision: addressRuntimeMeta.toolGateDecision ?? null, tool_gate_reason: addressRuntimeMeta.toolGateReason ?? null, normalized: null, normalizer_output: null }; return { handled: true, chatText, debug: attachAssistantMcpDiscoveryDebug(debug, { addressRuntimeMeta }) }; }