/** * Public interview projection for the Case GET payload. * * The tap card is shown only when the follow-up is a discriminator * (candidates already diverge or holdout) and the Agent wrote choice copy. */ import { askedKeysFromLedgerEvidence, buildCandidateContrastPacket, volunteeredDomainsFromEvidence, } from "../core/candidate-contrast-packet.ts"; import { evaluateCandidateSeparation } from "../core/candidate-separation.ts"; import { askedProbeKeysFromReceipt, previousInferenceFromReceipt } from "./inference-adapter"; import { latestUserStoppedCollecting, projectRectificationChoiceCard } from "./method-followup"; import { refinementFromDecisionReceipt } from "./refinement-packet"; import { internalObservationsFromWindowScan, windowScanFromDecisionReceipt, } from "./varga-observations"; import type { RectificationChoiceCard } from "./choice-card"; function candidateScoresFromDossier(latest: { decisionReceipt: Readonly> | null; candidates?: readonly Readonly<{ time: string; relativeSupport?: number; posterior_score?: number }>[]; } | null) { const inference = previousInferenceFromReceipt(latest?.decisionReceipt ?? null); if (inference && inference.candidates.length > 0) { return inference.candidates.map((item) => ({ time: item.time, score: item.posterior_score })); } return (latest?.candidates ?? []).map((item) => ({ time: item.time, score: item.relativeSupport ?? item.posterior_score ?? 0, })); } export function choiceCardFromCaseDossier(dossier: { evidence: readonly Readonly<{ status: string; domain: string; datePrecision: string; occurredFrom: string | null; occurredTo: string | null; eventKind?: string | null; summary?: string | null; }>[]; conversationSummary: { activeFocus: { id?: string; intent: string; targetDomain: string | null; targetKind: string | null; expectedAnswerSchema?: Readonly> | null; } | null; declinedSkippedTopics: readonly Readonly>[]; }; latestResult: { resultId?: string; decisionReceipt: Readonly> | null; selectionAllowed?: boolean; candidates?: readonly Readonly<{ time: string; relativeSupport?: number }>[]; } | null; case: { acceptedTime: string | null; }; turns?: readonly Readonly<{ role: string; text: string | null }>[]; }): RectificationChoiceCard | null { const windowScan = windowScanFromDecisionReceipt(dossier.latestResult?.decisionReceipt ?? null); const observations = internalObservationsFromWindowScan(windowScan); const refinement = refinementFromDecisionReceipt(dossier.latestResult?.decisionReceipt ?? null); const inference = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null); const candidateScores = candidateScoresFromDossier(dossier.latestResult); const askedProbeKeys = [ ...askedProbeKeysFromReceipt(dossier.latestResult?.decisionReceipt), ...askedKeysFromLedgerEvidence(dossier.evidence), ]; const contrastPacket = buildCandidateContrastPacket({ candidateSetVersion: inference?.candidate_set_id ?? dossier.latestResult?.resultId ?? "none", calculationResultId: dossier.latestResult?.resultId ?? null, engineProbes: refinement.discriminating_event_probes, vargaDifferences: [ ...(windowScan?.d9_candidates_differ && windowScan.d9_sign_names.length >= 2 ? [{ layer: "d9", signs: windowScan.d9_sign_names }] : []), ...(windowScan?.d10_candidates_differ && windowScan.d10_sign_names.length >= 2 ? [{ layer: "d10", signs: windowScan.d10_sign_names }] : []), ], candidateTimes: candidateScores.map((item) => item.time), transitions: windowScan?.transitions ?? [], askedKeys: askedProbeKeys, volunteeredDomains: volunteeredDomainsFromEvidence(dossier.evidence), }); const userStopped = latestUserStoppedCollecting(dossier.turns ?? []); return projectRectificationChoiceCard({ evidence: dossier.evidence, activeFocus: dossier.conversationSummary.activeFocus, declinedTopics: dossier.conversationSummary.declinedSkippedTopics, observations, sessionOutcome: dossier.case.acceptedTime ? "adopt_representative" : "collect_evidence", precisionStage: refinement.precision_stage?.current, nakshatraBoundary: refinement.nakshatra_boundary, oosBlindPrompts: refinement.oos_blind_prompts, eventProbes: refinement.discriminating_event_probes, askedProbeKeys, accepted: Boolean(dossier.case.acceptedTime), selectionAllowed: dossier.latestResult?.selectionAllowed === true, proposeAllowed: dossier.latestResult?.decisionReceipt?.propose_allowed === true, caseRevision: inference?.revision ?? 0, contrastPacket, candidateScores, userStopped, candidatesSeparated: evaluateCandidateSeparation(candidateScores).sufficient, }); }