import { z } from "zod"; const nonEmptyTextSchema = z.string().trim().min(1); export const legacyFindingItemSchema = z.object({ section: z.string(), finding: z.string(), suggestion: z.string(), }); export const v3FindingItemSchema = z.object({ skill_id: nonEmptyTextSchema, observation: nonEmptyTextSchema, customer_benefit: nonEmptyTextSchema, public_phrasing: nonEmptyTextSchema, severity: z.union([z.literal(1), z.literal(2), z.literal(3)]), evidence: nonEmptyTextSchema, applies: z.boolean(), }); export const findingItemSchema = legacyFindingItemSchema; export const auditFindingEvidenceRefSchema = z.object({ id: nonEmptyTextSchema, type: z.enum([ "crawl_page", "technical_check", "screenshot", "pagespeed", "jina_excerpt", "generation_stage", ]), label: nonEmptyTextSchema, sourceUrl: z.string().trim(), }); export const auditSpecialistFindingSchema = z .object({ skillId: nonEmptyTextSchema, claim: nonEmptyTextSchema, recommendation: nonEmptyTextSchema, customerBenefit: nonEmptyTextSchema, severity: z.union([z.literal(1), z.literal(2), z.literal(3)]), confidence: z.number().min(0).max(1), evidenceRefs: z.array(auditFindingEvidenceRefSchema).min(1), applies: z.boolean(), unknowns: z.array(z.string()), }) .superRefine((finding, ctx) => { const combined = [ finding.claim, finding.recommendation, finding.customerBenefit, ].join(" "); if (/\bunbekannt\b|\bunknown\b/i.test(combined)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "unknown-only findings are not valid audit claims", path: ["claim"], }); } }); export const auditSpecialistResultSchema = z.object({ status: z.enum(["success", "partial", "skipped", "failed"]), findings: z.array(auditSpecialistFindingSchema), notes: z.array(z.string()), }); export const auditRejectedFindingSchema = z.object({ findingId: nonEmptyTextSchema, skillId: nonEmptyTextSchema, claim: nonEmptyTextSchema, rejectionReason: nonEmptyTextSchema, }); export const auditEvidenceVerificationSchema = z.object({ verifiedFindingIds: z.array(nonEmptyTextSchema), rejectedFindings: z.array(auditRejectedFindingSchema), contradictions: z.array(z.string()), notes: z.array(z.string()), }); export const internalFindingsSchema = z.object({ findings: z.array(findingItemSchema), summary: z.string(), }); export const auditClassificationSchema = z.object({ findings: z.array(v3FindingItemSchema).min(1), summary: nonEmptyTextSchema, usedSkills: z.array(nonEmptyTextSchema).nullable(), }); export const auditGenerationResultSchema = z.object({ findings: z.array(v3FindingItemSchema).min(1), usedSkills: z.array(nonEmptyTextSchema).min(1), publicAuditText: nonEmptyTextSchema, finalSummary: nonEmptyTextSchema, emailSubject: nonEmptyTextSchema, emailBody: nonEmptyTextSchema, phoneScript: nonEmptyTextSchema, ctaType: z.enum(["anruf", "termin", "rueckruf"]), }); export const auditSummarySchema = z.object({ summary: z.string(), keyFindings: z.array(z.string()), }); export const publicAuditTextSchema = z.object({ publicText: z.string(), }); export const emailDraftSchema = z.object({ body: z.string(), }); export const emailSubjectSchema = z.object({ subject: z.string(), }); export const callScriptSchema = z.object({ openingLine: z.string(), callScript: z.array(z.string()), closeLine: z.string(), }); export const followUpDraftSchema = z.object({ message: z.string(), followInDays: z.number().int().min(0).nullable(), goals: z.array(z.string()).nullable(), }); export const qualityReviewSchema = z.object({ isValid: z.boolean(), issues: z.array(z.string()), suggestions: z.array(z.string()), notes: z.array(z.string()).nullable(), }); export type FindingItem = z.infer; export type V3FindingItem = z.infer; export type AuditFindingEvidenceRef = z.infer; export type AuditSpecialistFinding = z.infer; export type AuditSpecialistResult = z.infer; export type AuditEvidenceVerification = z.infer; export type InternalFindings = z.infer; export type AuditClassification = z.infer; export type AuditGenerationResult = z.infer; export type AuditSummary = z.infer; export type PublicAuditText = z.infer; export type EmailDraft = z.infer; export type EmailSubject = z.infer; export type CallScript = z.infer; export type FollowUpDraft = z.infer; export type QualityReview = z.infer;