02d4c91626
Occupation answers were stored as career, so coverage never closed, yearless cards never fired, and the decision layer suppressed an engine-allowed range. Normalize occupation-collect writes, allow yearless cards once training is open, offer a range when no distinguish card remains, and align the public house table to the representative minute. Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
/**
|
|
* 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.
|
|
* Card identity is the persisted focus UUID plus the inference revision.
|
|
*/
|
|
|
|
import { previousInferenceFromReceipt } from "./inference-adapter";
|
|
import {
|
|
decideFromDossier,
|
|
rectificationFollowupCatalog,
|
|
} from "./decision-from-dossier";
|
|
import { evidenceLedgerFingerprint } from "./tool-service";
|
|
import { projectRectificationChoiceCard } from "./method-followup";
|
|
import {
|
|
internalObservationsFromWindowScan,
|
|
windowScanFromDecisionReceipt,
|
|
} from "./varga-observations";
|
|
import type { RectificationChoiceCard } from "./choice-card";
|
|
|
|
export function choiceCardFromCaseDossier(dossier: {
|
|
evidence: readonly Readonly<{
|
|
id?: string;
|
|
status: string;
|
|
domain: string;
|
|
datePrecision: string;
|
|
occurredFrom: string | null;
|
|
occurredTo: string | null;
|
|
eventKind?: string | null;
|
|
summary?: string | null;
|
|
}>[];
|
|
conversationSummary: {
|
|
activeFocus: {
|
|
id?: string;
|
|
questionId?: string;
|
|
intent: string;
|
|
targetDomain: string | null;
|
|
targetKind: string | null;
|
|
expectedAnswerSchema?: Readonly<Record<string, unknown>> | null;
|
|
} | null;
|
|
declinedSkippedTopics: readonly Readonly<Record<string, unknown>>[];
|
|
};
|
|
latestResult: {
|
|
resultId?: string;
|
|
decisionReceipt: Readonly<Record<string, unknown>> | null;
|
|
selectionAllowed?: boolean;
|
|
confirmationAllowed?: boolean;
|
|
candidates?: readonly Readonly<{
|
|
time: string;
|
|
rank?: number;
|
|
tiedMinuteCount?: number;
|
|
relativeSupport?: number;
|
|
}>[];
|
|
evidenceLedgerFingerprint?: string | null;
|
|
} | null;
|
|
case: {
|
|
acceptedTime: string | null;
|
|
status?: string;
|
|
};
|
|
turns?: readonly Readonly<{ role: string; text: string | null }>[];
|
|
}): RectificationChoiceCard | null {
|
|
const catalog = rectificationFollowupCatalog(dossier.latestResult, dossier.evidence);
|
|
const observations = internalObservationsFromWindowScan(
|
|
windowScanFromDecisionReceipt(dossier.latestResult?.decisionReceipt ?? null),
|
|
);
|
|
const inference = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null);
|
|
const decision = decideFromDossier(dossier, {
|
|
currentEvidenceFingerprint: evidenceLedgerFingerprint(dossier.evidence as never),
|
|
});
|
|
const latestAssistantText = [...(dossier.turns ?? [])]
|
|
.reverse()
|
|
.find((turn) => turn.role === "assistant")
|
|
?.text ?? null;
|
|
return projectRectificationChoiceCard({
|
|
evidence: dossier.evidence,
|
|
activeFocus: dossier.conversationSummary.activeFocus,
|
|
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
|
|
closedCollectFocuses: dossier.conversationSummary.declinedSkippedTopics,
|
|
observations,
|
|
sessionOutcome: decision.sessionOutcome,
|
|
...catalog,
|
|
precisionStage: decision.precisionStage === "collect_events"
|
|
? "collect_events"
|
|
: decision.precisionStage === "ready_to_adopt"
|
|
? "ready_to_adopt"
|
|
: catalog.precisionStage,
|
|
accepted: Boolean(dossier.case.acceptedTime),
|
|
selectionAllowed: decision.selectionAllowed,
|
|
proposeAllowed: decision.proposeAllowed,
|
|
confirmationAllowed: decision.canConfirmExactMinute,
|
|
caseRevision: inference?.revision ?? 0,
|
|
candidateScores: decision.separation.ranked.map((item) => ({
|
|
time: item.time,
|
|
score: item.score,
|
|
})),
|
|
userStopped: dossier.case.status === "paused",
|
|
latestAssistantText,
|
|
candidatesSeparated: decision.separation.sufficient,
|
|
holdoutValidation: decision.holdoutValidation,
|
|
});
|
|
}
|
|
|
|
export { decideFromDossier, overlayPublicDecision } from "./decision-from-dossier";
|