import type { AssistantAddressLaneLike, AssistantAddressFollowupCarryoverLike } from "./assistantAddressLaneRuntimeAdapter"; import type { AssistantMessageResponsePayload, AssistantReplyType } from "../types/assistant"; import type { AddressExecutionDebug } from "../types/addressQuery"; import { finalizeAssistantAddressTurn, type AddressCarryoverMetaLogInput, type AddressLlmPreDecomposeMetaLogInput, type FinalizeAssistantAddressTurnInput } from "./assistantAddressTurnFinalizeRuntimeAdapter"; import { applyAssistantCapabilityBindingResponseGuard } from "./assistantCapabilityBindingResponseGuard"; import { attachAssistantCapabilityRuntimeBinding } from "./assistantCapabilityRuntimeBindingAdapter"; import { attachAssistantMcpDiscoveryDebug } from "./assistantMcpDiscoveryDebugAttachment"; import { applyAssistantMcpDiscoveryResponsePolicy } from "./assistantMcpDiscoveryResponsePolicy"; import { attachAssistantRuntimeContractShadow } from "./assistantRuntimeContractResolver"; import { attachAssistantStateTransition } from "./assistantStateTransitionRuntimeAdapter"; import { attachAssistantTruthAnswerPolicy } from "./assistantTruthAnswerPolicyRuntimeAdapter"; export interface RunAssistantAddressLaneResponseRuntimeInput { sessionId: string; userMessage: string; effectiveAddressUserMessage: string; addressLane: AssistantAddressLaneLike; carryoverMeta?: AssistantAddressFollowupCarryoverLike | null; llmPreDecomposeMeta?: Record | null; knownOrganizations: string[]; activeOrganization: string | null; sanitizeOutgoingAssistantText: (text: unknown, fallback?: string) => string; buildAddressDebugPayload: ( addressDebug: unknown, llmPreDecomposeMeta?: Record | null ) => Record; buildAddressFollowupOffer: (addressDebug: Record) => unknown; mergeKnownOrganizations: (organizations: string[]) => string[]; toNonEmptyString: (value: unknown) => string | null; appendItem: FinalizeAssistantAddressTurnInput["appendItem"]; getSession: FinalizeAssistantAddressTurnInput["getSession"]; persistSession: FinalizeAssistantAddressTurnInput["persistSession"]; cloneConversation: FinalizeAssistantAddressTurnInput["cloneConversation"]; logEvent: FinalizeAssistantAddressTurnInput["logEvent"]; messageIdFactory: FinalizeAssistantAddressTurnInput["messageIdFactory"]; finalizeAddressTurn?: ( input: FinalizeAssistantAddressTurnInput ) => { response: ResponseType; }; } export interface RunAssistantAddressLaneResponseRuntimeOutput { response: ResponseType; debug: Record; } function toRecordObject(value: unknown): Record | null { if (!value || typeof value !== "object") { return null; } return value as Record; } function toNullableString(value: unknown): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function toNullableBoolean(value: unknown): boolean | undefined { if (typeof value === "boolean") { return value; } return undefined; } function normalizeAddressReplyType(value: unknown): AssistantReplyType { return value === "factual" || value === "partial_coverage" ? value : "partial_coverage"; } function normalizeAddressLaneDebug(value: unknown): AddressExecutionDebug { return (toRecordObject(value) ?? {}) as unknown as AddressExecutionDebug; } function normalizeCarryoverMeta(value: unknown): AddressCarryoverMetaLogInput | null { const source = toRecordObject(value); if (!source) { return null; } const followupContext = toRecordObject(source.followupContext); const previousAddressIntent = toNullableString(source.previousAddressIntent) ?? toNullableString(followupContext?.previous_intent); const previousAddressAnchor = toNullableString(source.previousAddressAnchor) ?? toNullableString(followupContext?.previous_anchor); if (!previousAddressIntent && !previousAddressAnchor) { return null; } return { previousAddressIntent, previousAddressAnchor }; } function normalizeLlmPreDecomposeMeta(value: unknown): AddressLlmPreDecomposeMetaLogInput | null { const source = toRecordObject(value); if (!source) { return null; } const dialogContinuationContractRaw = toRecordObject(source.dialogContinuationContract); const addressRetryAuditRaw = toRecordObject(source.addressRetryAudit); const predecomposeContractRaw = toRecordObject(source.predecomposeContract); const predecomposePeriodRaw = toRecordObject(predecomposeContractRaw?.period); const semanticExtractionContractRaw = toRecordObject(source.semanticExtractionContract); const normalized: AddressLlmPreDecomposeMetaLogInput = {}; const attempted = toNullableBoolean(source.attempted); if (attempted !== undefined) normalized.attempted = attempted; const applied = toNullableBoolean(source.applied); if (applied !== undefined) normalized.applied = applied; const provider = toNullableString(source.provider); if (provider) normalized.provider = provider; const traceId = toNullableString(source.traceId); if (traceId) normalized.traceId = traceId; const reason = toNullableString(source.reason); if (reason) normalized.reason = reason; const fallbackRuleHit = toNullableString(source.fallbackRuleHit); if (fallbackRuleHit) normalized.fallbackRuleHit = fallbackRuleHit; const sanitizedUserMessage = toNullableString(source.sanitizedUserMessage); if (sanitizedUserMessage) normalized.sanitizedUserMessage = sanitizedUserMessage; const toolGateDecision = toNullableString(source.toolGateDecision); if (toolGateDecision) normalized.toolGateDecision = toolGateDecision; const toolGateReason = toNullableString(source.toolGateReason); if (toolGateReason) normalized.toolGateReason = toolGateReason; if (dialogContinuationContractRaw) { const decision = toNullableString(dialogContinuationContractRaw.decision); const targetIntent = toNullableString(dialogContinuationContractRaw.target_intent); if (decision || targetIntent) { normalized.dialogContinuationContract = { decision, target_intent: targetIntent }; } } if (addressRetryAuditRaw) { const retryAttempted = toNullableBoolean(addressRetryAuditRaw.attempted); const retryReason = toNullableString(addressRetryAuditRaw.reason); const initialLimitedCategory = toNullableString(addressRetryAuditRaw.initial_limited_category); const retryResultCategory = toNullableString(addressRetryAuditRaw.retry_result_category); if ( retryAttempted !== undefined || retryReason || initialLimitedCategory || retryResultCategory ) { normalized.addressRetryAudit = { attempted: retryAttempted, reason: retryReason, initial_limited_category: initialLimitedCategory, retry_result_category: retryResultCategory }; } } if (predecomposeContractRaw) { const intent = toNullableString(predecomposeContractRaw.intent); const aggregationProfile = toNullableString(predecomposeContractRaw.aggregation_profile); const periodScope = toNullableString(predecomposePeriodRaw?.scope); if (intent || aggregationProfile || periodScope) { normalized.predecomposeContract = { intent, aggregation_profile: aggregationProfile, period: periodScope ? { scope: periodScope } : null }; } } if (semanticExtractionContractRaw) { const valid = toNullableBoolean(semanticExtractionContractRaw.valid); const quality = toNullableString(semanticExtractionContractRaw.quality); const applyCanonicalRecommended = toNullableBoolean( semanticExtractionContractRaw.apply_canonical_recommended ); const reasonCodes = Array.isArray(semanticExtractionContractRaw.reason_codes) ? semanticExtractionContractRaw.reason_codes .map((item) => toNullableString(item)) .filter((item): item is string => Boolean(item)) : []; if (valid !== undefined || quality || applyCanonicalRecommended !== undefined || reasonCodes.length > 0) { normalized.semanticExtractionContract = { valid: valid ?? null, quality, apply_canonical_recommended: applyCanonicalRecommended ?? null, reason_codes: reasonCodes }; } } const hasUsefulField = Object.values(normalized).some((item) => item !== undefined && item !== null); return hasUsefulField ? normalized : null; } export function runAssistantAddressLaneResponseRuntime( input: RunAssistantAddressLaneResponseRuntimeInput ): RunAssistantAddressLaneResponseRuntimeOutput { const finalizeAddressTurnSafe = input.finalizeAddressTurn ?? finalizeAssistantAddressTurn; const safeAddressReply = input.sanitizeOutgoingAssistantText(input.addressLane.reply_text); const debug = input.buildAddressDebugPayload(input.addressLane.debug, input.llmPreDecomposeMeta); const followupOffer = input.buildAddressFollowupOffer(debug); if (followupOffer) { debug.address_followup_offer = followupOffer; } const laneOrganizationCandidates = Array.isArray(input.addressLane.debug?.organization_candidates) ? input.addressLane.debug.organization_candidates : []; const debugKnownOrganizations = input.mergeKnownOrganizations([ ...input.knownOrganizations, ...laneOrganizationCandidates ]); const debugFilters = debug?.extracted_filters && typeof debug.extracted_filters === "object" ? (debug.extracted_filters as Record) : null; const debugActiveOrganization = input.toNonEmptyString(debugFilters?.organization) ?? input.toNonEmptyString(input.activeOrganization); const followupContextSource = input.carryoverMeta?.followupContext && typeof input.carryoverMeta.followupContext === "object" ? (input.carryoverMeta.followupContext as Record) : null; if (debugKnownOrganizations.length > 0) { debug.assistant_known_organizations = debugKnownOrganizations; } if (debugActiveOrganization) { debug.assistant_active_organization = debugActiveOrganization; } const rootIntent = input.toNonEmptyString(followupContextSource?.root_intent); const currentFrameKind = input.toNonEmptyString(followupContextSource?.current_frame_kind); const rootFilters = followupContextSource?.root_filters && typeof followupContextSource.root_filters === "object" ? (followupContextSource.root_filters as Record) : null; if (rootIntent || currentFrameKind) { debug.address_root_frame_context = { root_intent: rootIntent, current_frame_kind: currentFrameKind, organization: input.toNonEmptyString(rootFilters?.organization), as_of_date: input.toNonEmptyString(rootFilters?.as_of_date), period_from: input.toNonEmptyString(rootFilters?.period_from), period_to: input.toNonEmptyString(rootFilters?.period_to) }; } const debugWithRuntimeContracts = attachAssistantRuntimeContractShadow(debug, { userMessage: input.userMessage, addressRuntimeMeta: input.llmPreDecomposeMeta }); const debugWithTruthAnswerPolicy = attachAssistantTruthAnswerPolicy(debugWithRuntimeContracts, { addressRuntimeMeta: input.llmPreDecomposeMeta, replyType: normalizeAddressReplyType(input.addressLane.reply_type) }); const debugWithStateTransition = attachAssistantStateTransition(debugWithTruthAnswerPolicy, { addressRuntimeMeta: input.llmPreDecomposeMeta, replyType: normalizeAddressReplyType(input.addressLane.reply_type) }); const debugWithCapabilityBinding = attachAssistantCapabilityRuntimeBinding(debugWithStateTransition, { addressRuntimeMeta: input.llmPreDecomposeMeta, replyType: normalizeAddressReplyType(input.addressLane.reply_type) }); const debugWithMcpDiscovery = attachAssistantMcpDiscoveryDebug(debugWithCapabilityBinding, { addressRuntimeMeta: input.llmPreDecomposeMeta }); const guardedResponse = applyAssistantCapabilityBindingResponseGuard({ assistantReply: safeAddressReply, replyType: normalizeAddressReplyType(input.addressLane.reply_type), capabilityBinding: debugWithMcpDiscovery.assistant_capability_binding_v1 }); const debugWithResponseGuard = { ...debugWithMcpDiscovery, capability_binding_response_guard: guardedResponse.audit }; const mcpDiscoveryResponsePolicy = applyAssistantMcpDiscoveryResponsePolicy({ currentReply: guardedResponse.assistantReply, currentReplySource: "address_query_runtime_v1", currentReplyType: guardedResponse.replyType, addressRuntimeMeta: debugWithResponseGuard }); const finalAssistantReply = mcpDiscoveryResponsePolicy.applied ? mcpDiscoveryResponsePolicy.reply_text : guardedResponse.assistantReply; const finalReplyType = mcpDiscoveryResponsePolicy.applied ? "partial_coverage" : guardedResponse.replyType; const finalDebug = { ...debugWithResponseGuard, mcp_discovery_response_policy_v1: mcpDiscoveryResponsePolicy, mcp_discovery_response_candidate_v1: mcpDiscoveryResponsePolicy.candidate, mcp_discovery_response_applied: mcpDiscoveryResponsePolicy.applied }; const finalization = finalizeAddressTurnSafe({ sessionId: input.sessionId, userMessage: input.userMessage, effectiveAddressUserMessage: input.effectiveAddressUserMessage, assistantReply: finalAssistantReply, replyType: finalReplyType, addressLaneDebug: normalizeAddressLaneDebug(input.addressLane.debug), debug: finalDebug, carryoverMeta: normalizeCarryoverMeta(input.carryoverMeta), llmPreDecomposeMeta: normalizeLlmPreDecomposeMeta(input.llmPreDecomposeMeta), appendItem: input.appendItem, getSession: input.getSession, persistSession: input.persistSession, cloneConversation: input.cloneConversation, logEvent: input.logEvent, messageIdFactory: input.messageIdFactory }); return { response: finalization.response as ResponseType, debug: finalDebug }; }