337 lines
13 KiB
TypeScript
337 lines
13 KiB
TypeScript
import { z } from "zod";
|
|
|
|
export const rectificationV4Protocol = "rectification-evidence-v4" as const;
|
|
export const rectificationAgentV5Protocol = "rectification-evidence-v5" as const;
|
|
export const rectificationDeploymentModeSchema = z.enum(["v4_legacy", "v5_shadow", "v5_agent"]);
|
|
export type RectificationDeploymentMode = z.infer<typeof rectificationDeploymentModeSchema>;
|
|
export const rectificationV4AlgorithmVersion = "rectification-v5-matrix-scoring-1" as const;
|
|
|
|
export const rectificationV4CaseStatusSchema = z.enum([
|
|
"awaiting_answer",
|
|
"processing",
|
|
"range_ready",
|
|
"paused",
|
|
"abandoned",
|
|
]);
|
|
export type RectificationV4CaseStatus = z.infer<typeof rectificationV4CaseStatusSchema>;
|
|
|
|
export const rectificationV4PhaseSchema = z.enum([
|
|
"collecting_evidence",
|
|
"extracting_evidence",
|
|
"scoring_candidates",
|
|
"checking_robustness",
|
|
"planning_question",
|
|
"reasoning",
|
|
"rendering",
|
|
"complete",
|
|
]);
|
|
export type RectificationV4Phase = z.infer<typeof rectificationV4PhaseSchema>;
|
|
|
|
export const evidenceDomainSchema = z.enum([
|
|
"education",
|
|
"relocation",
|
|
"relationship",
|
|
"career",
|
|
"finance",
|
|
"health_pressure",
|
|
"family",
|
|
"other",
|
|
]);
|
|
export type EvidenceDomain = z.infer<typeof evidenceDomainSchema>;
|
|
|
|
export const eventKindSchema = z.enum([
|
|
"education_milestone",
|
|
"relocation",
|
|
"relationship_start",
|
|
"relationship_end",
|
|
"career_change",
|
|
"finance_change",
|
|
"self_health_event",
|
|
"family_health_event",
|
|
"family_bereavement",
|
|
"relationship_change",
|
|
"family_event",
|
|
"other",
|
|
]);
|
|
export type EventKind = z.infer<typeof eventKindSchema>;
|
|
|
|
export const datePrecisionSchema = z.enum(["day", "month", "quarter", "year", "range"]);
|
|
export type DatePrecision = z.infer<typeof datePrecisionSchema>;
|
|
|
|
export const calendarDateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
export const clockTimeSchema = z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/);
|
|
|
|
export const eventDateRangeSchema = z.object({
|
|
start: calendarDateSchema,
|
|
end: calendarDateSchema,
|
|
precision: datePrecisionSchema,
|
|
label: z.string().trim().min(1).max(80),
|
|
}).strict().superRefine((value, context) => {
|
|
if (value.start > value.end) {
|
|
context.addIssue({ code: "custom", message: "event date range start must not exceed end" });
|
|
}
|
|
});
|
|
export type EventDateRange = z.infer<typeof eventDateRangeSchema>;
|
|
|
|
export const eventSubjectSchema = z.enum(["self", "family", "partner", "other"]);
|
|
export type EventSubject = z.infer<typeof eventSubjectSchema>;
|
|
|
|
export const relatedPersonSchema = z.enum(["father", "mother", "grandparent", "sibling", "partner"]);
|
|
export type RelatedPerson = z.infer<typeof relatedPersonSchema>;
|
|
|
|
export const scoreabilitySchema = z.enum(["scoreable", "context_only", "pending_review", "unsupported"]);
|
|
export type Scoreability = z.infer<typeof scoreabilitySchema>;
|
|
|
|
export const lifeEventRevisionSchema = z.object({
|
|
id: z.string().uuid(),
|
|
eventId: z.string().uuid(),
|
|
revision: z.number().int().positive(),
|
|
domain: evidenceDomainSchema,
|
|
eventKind: eventKindSchema,
|
|
subject: eventSubjectSchema,
|
|
relatedPerson: relatedPersonSchema.nullable(),
|
|
summary: z.string().trim().min(1).max(1_000),
|
|
rawText: z.string().trim().min(1).max(4_000),
|
|
dateRange: eventDateRangeSchema,
|
|
scoreability: scoreabilitySchema,
|
|
supersedesRevisionId: z.string().uuid().nullable(),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
}).strict();
|
|
export type LifeEventRevision = z.infer<typeof lifeEventRevisionSchema>;
|
|
|
|
export const pendingEvidenceSchema = z.object({
|
|
id: z.string().uuid(),
|
|
caseId: z.string().uuid(),
|
|
turnId: z.string().uuid(),
|
|
rawText: z.string().trim().min(1).max(4_000),
|
|
reasonCode: z.enum(["date_unresolved", "event_unparsed"]),
|
|
targetEventId: z.string().uuid().nullable(),
|
|
resolvedEventId: z.string().uuid().nullable(),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
resolvedAt: z.string().datetime({ offset: true }).nullable(),
|
|
}).strict();
|
|
export type PendingEvidence = z.infer<typeof pendingEvidenceSchema>;
|
|
|
|
export const calculationSpecSchema = z.object({
|
|
version: z.literal("rectification-calculation-spec-v4"),
|
|
birthDate: calendarDateSchema,
|
|
candidateRange: z.object({ start: clockTimeSchema, end: clockTimeSchema }).strict(),
|
|
latitude: z.number().finite().min(-90).max(90),
|
|
longitude: z.number().finite().min(-180).max(180),
|
|
timezoneOffsetHours: z.number().finite().min(-14).max(14),
|
|
ayanamsa: z.literal("lahiri"),
|
|
nodeMode: z.literal("mean"),
|
|
minuteStep: z.literal(1),
|
|
}).strict();
|
|
export type CalculationSpec = z.infer<typeof calculationSpecSchema>;
|
|
|
|
export const candidateMinuteSchema = z.object({
|
|
time: clockTimeSchema,
|
|
score: z.number().finite(),
|
|
supportingEventIds: z.array(z.string().uuid()),
|
|
conflictingEventIds: z.array(z.string().uuid()),
|
|
}).strict();
|
|
export type CandidateMinute = z.infer<typeof candidateMinuteSchema>;
|
|
|
|
export const candidateClusterSchema = z.object({
|
|
rank: z.number().int().positive(),
|
|
startTime: clockTimeSchema,
|
|
endTime: clockTimeSchema,
|
|
representativeTime: clockTimeSchema,
|
|
widthMinutes: z.number().int().positive(),
|
|
peakScore: z.number().finite(),
|
|
scoreMass: z.number().finite().nonnegative(),
|
|
}).strict();
|
|
export type CandidateCluster = z.infer<typeof candidateClusterSchema>;
|
|
|
|
export const robustnessSchema = z.object({
|
|
neighborSupportMinutes: z.number().int().nonnegative(),
|
|
leaveOneOutRetentionRate: z.number().finite().min(0).max(1),
|
|
dateSensitivityRetentionRate: z.number().finite().min(0).max(1),
|
|
calculationSpecHashMatched: z.boolean(),
|
|
}).strict();
|
|
export type Robustness = z.infer<typeof robustnessSchema>;
|
|
|
|
export const candidateSnapshotSchema = z.object({
|
|
id: z.string().uuid(),
|
|
caseId: z.string().uuid(),
|
|
caseVersion: z.number().int().nonnegative(),
|
|
evidenceSetHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
calculationSpecHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
algorithmVersion: z.literal(rectificationV4AlgorithmVersion),
|
|
candidates: z.array(candidateMinuteSchema).min(1).max(1_440),
|
|
clusters: z.array(candidateClusterSchema).max(20),
|
|
robustness: robustnessSchema,
|
|
canConfirmExactMinute: z.literal(false),
|
|
canAcceptRange: z.boolean(),
|
|
gateReasons: z.array(z.string().trim().min(1).max(120)).max(20),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
}).strict();
|
|
export type CandidateSnapshot = z.infer<typeof candidateSnapshotSchema>;
|
|
|
|
export const rectificationV4QuestionSchema = z.object({
|
|
id: z.string().uuid(),
|
|
domain: evidenceDomainSchema,
|
|
targetEventId: z.string().uuid().nullable(),
|
|
prompt: z.string().trim().min(1).max(1_000),
|
|
recallCost: z.enum(["low", "medium", "high"]),
|
|
reason: z.string().trim().min(1).max(240),
|
|
}).strict();
|
|
export type RectificationV4Question = z.infer<typeof rectificationV4QuestionSchema>;
|
|
|
|
export const rectificationV4TurnSchema = z.object({
|
|
id: z.string().uuid(),
|
|
caseId: z.string().uuid(),
|
|
caseVersion: z.number().int().positive(),
|
|
questionId: z.string().uuid().nullable(),
|
|
questionDomain: evidenceDomainSchema.nullable(),
|
|
questionTargetEventId: z.string().uuid().nullable(),
|
|
question: z.string().trim().min(1).max(1_000),
|
|
answer: z.string().max(4_000),
|
|
modelId: z.string().trim().min(1).max(120).nullable(),
|
|
actionId: z.string().uuid(),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
}).strict();
|
|
export type RectificationV4Turn = z.infer<typeof rectificationV4TurnSchema>;
|
|
|
|
export const rectificationV4CaseSchema = z.object({
|
|
id: z.string().uuid(),
|
|
userId: z.string().uuid(),
|
|
protocol: z.union([z.literal(rectificationV4Protocol), z.literal(rectificationAgentV5Protocol)]),
|
|
version: z.number().int().nonnegative(),
|
|
status: rectificationV4CaseStatusSchema,
|
|
phase: rectificationV4PhaseSchema,
|
|
calculationSpec: calculationSpecSchema,
|
|
calculationSpecHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
evidenceSetHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
currentQuestion: rectificationV4QuestionSchema.nullable(),
|
|
latestSnapshot: candidateSnapshotSchema.nullable(),
|
|
orchestrationModelId: z.string().trim().min(1).max(120).nullable(),
|
|
narrationModelId: z.string().trim().min(1).max(120).nullable(),
|
|
skillVersion: z.string().trim().min(1).max(120),
|
|
promptVersion: z.string().trim().min(1).max(120),
|
|
algorithmVersion: z.string().trim().min(1).max(120),
|
|
deploymentMode: rectificationDeploymentModeSchema,
|
|
agentMode: z.enum(["agent", "deterministic_fallback"]),
|
|
featureSnapshotId: z.string().uuid().nullable(),
|
|
latestDiagnosticsId: z.string().uuid().nullable(),
|
|
acceptedRange: z.object({ start: clockTimeSchema, end: clockTimeSchema }).strict().nullable(),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
updatedAt: z.string().datetime({ offset: true }),
|
|
}).strict();
|
|
export type RectificationV4Case = z.infer<typeof rectificationV4CaseSchema>;
|
|
|
|
export const createCaseRequestSchema = z.object({
|
|
actionId: z.string().uuid(),
|
|
uncertaintyMinutes: z.number().int().min(5).max(720).optional(),
|
|
}).strict();
|
|
|
|
export const answerRequestSchema = z.object({
|
|
actionId: z.string().uuid(),
|
|
expectedCaseVersion: z.number().int().nonnegative(),
|
|
answer: z.string().trim().min(1).max(4_000),
|
|
modelId: z.string().trim().min(1).max(120).nullable().optional(),
|
|
}).strict();
|
|
|
|
export const reviseEventRequestSchema = z.object({
|
|
actionId: z.string().uuid(),
|
|
expectedCaseVersion: z.number().int().nonnegative(),
|
|
domain: evidenceDomainSchema,
|
|
eventKind: eventKindSchema,
|
|
subject: eventSubjectSchema,
|
|
relatedPerson: relatedPersonSchema.nullable(),
|
|
summary: z.string().trim().min(1).max(1_000),
|
|
rawText: z.string().trim().min(1).max(4_000),
|
|
dateRange: eventDateRangeSchema,
|
|
scoreability: scoreabilitySchema.optional(),
|
|
}).strict();
|
|
|
|
export const caseActionRequestSchema = z.object({
|
|
actionId: z.string().uuid(),
|
|
expectedCaseVersion: z.number().int().nonnegative(),
|
|
}).strict();
|
|
|
|
export const acceptRangeRequestSchema = caseActionRequestSchema.extend({
|
|
startTime: clockTimeSchema,
|
|
endTime: clockTimeSchema,
|
|
}).strict();
|
|
|
|
export const rectificationV4JobSchema = z.object({
|
|
id: z.string().uuid(),
|
|
caseId: z.string().uuid(),
|
|
status: z.enum(["pending", "processing", "completed", "failed", "stale"]),
|
|
phase: rectificationV4PhaseSchema,
|
|
expectedCaseVersion: z.number().int().nonnegative(),
|
|
evidenceSetHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
calculationSpecHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
errorCode: z.string().trim().min(1).max(120).nullable(),
|
|
createdAt: z.string().datetime({ offset: true }),
|
|
updatedAt: z.string().datetime({ offset: true }),
|
|
}).strict();
|
|
export type RectificationV4Job = z.infer<typeof rectificationV4JobSchema>;
|
|
|
|
export const rectificationAnalysisStageSchema = z.object({
|
|
phase: z.enum([
|
|
"extracting_evidence",
|
|
"scoring_candidates",
|
|
"checking_robustness",
|
|
"planning_question",
|
|
"reasoning",
|
|
"rendering",
|
|
]),
|
|
label: z.string().trim().min(1).max(120),
|
|
status: z.enum(["completed", "failed"]),
|
|
durationMs: z.number().int().min(0).max(300_000).nullable(),
|
|
}).strict();
|
|
|
|
export const rectificationAnalysisToolCallSchema = z.object({
|
|
category: z.enum(["candidate_engine", "diagnostic", "agent_diagnostic"]),
|
|
label: z.string().trim().min(1).max(120),
|
|
outcome: z.enum(["succeeded", "failed", "rejected"]),
|
|
durationMs: z.number().int().min(0).max(300_000).nullable(),
|
|
}).strict();
|
|
|
|
export const rectificationAnalysisTraceSchema = z.object({
|
|
status: z.enum(["completed", "failed", "legacy"]),
|
|
stages: z.array(rectificationAnalysisStageSchema).max(12),
|
|
toolCalls: z.array(rectificationAnalysisToolCallSchema).max(16),
|
|
techniques: z.array(z.string().trim().min(1).max(120)).max(24),
|
|
reasoningSummary: z.string().trim().min(1).max(500).nullable(),
|
|
reasoningSource: z.enum(["provider_summary", "none"]),
|
|
}).strict();
|
|
export type RectificationAnalysisTrace = z.infer<typeof rectificationAnalysisTraceSchema>;
|
|
|
|
export const rectificationAnalysisItemSchema = z.object({
|
|
sourceTurnId: z.string().uuid(),
|
|
trace: rectificationAnalysisTraceSchema,
|
|
}).strict();
|
|
export type RectificationAnalysisItem = z.infer<typeof rectificationAnalysisItemSchema>;
|
|
|
|
export const rectificationV4ApiResponseSchema = z.object({
|
|
case: rectificationV4CaseSchema,
|
|
job: rectificationV4JobSchema.nullable(),
|
|
events: z.array(lifeEventRevisionSchema),
|
|
turns: z.array(rectificationV4TurnSchema),
|
|
analysis: z.array(rectificationAnalysisItemSchema).optional(),
|
|
}).strict();
|
|
export type RectificationV4ApiResponse = z.infer<typeof rectificationV4ApiResponseSchema>;
|
|
|
|
export const rectificationV4HandoffStatusSchema = z.enum([
|
|
"pending",
|
|
"claimed",
|
|
"in_progress",
|
|
"consumed",
|
|
]);
|
|
|
|
export const rectificationV4HandoffSchema = z.object({
|
|
protocol: z.union([z.literal(rectificationV4Protocol), z.literal(rectificationAgentV5Protocol)]),
|
|
caseId: z.string().uuid(),
|
|
caseVersion: z.number().int().nonnegative(),
|
|
question: z.string().trim().min(1).max(500),
|
|
questionFingerprint: z.string().regex(/^[0-9a-f]{64}$/),
|
|
requestId: z.string().uuid(),
|
|
status: rectificationV4HandoffStatusSchema,
|
|
acceptedRange: z.object({ start: clockTimeSchema, end: clockTimeSchema }).strict().nullable(),
|
|
}).strict();
|
|
export type RectificationV4Handoff = z.infer<typeof rectificationV4HandoffSchema>;
|