import { z } from "zod"; export const ORCHESTRATOR_VERSION = "orchestrator-v1"; export const photoRoles = ["hero", "packshot_front", "packshot_34", "side", "back", "sleep", "storage", "detail"] as const; const id = z.string().min(1).max(160); const sha = z.string().regex(/^[a-f0-9]{64}$/); const key = z.string().regex(/^[a-z][a-z0-9_]*(?:\.[a-z0-9_]+)*$/).max(200); const sourceKind = z.enum(["manufacturer_sheet", "catalog_card", "owner", "measurement", "derived", "brief", "api_readback"]); export const actionSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("set_fact"), key, value: z.json().refine((v) => JSON.stringify(v).length <= 8000, "Wartość jest zbyt długa"), unit: z.string().max(20).nullable().default(null), status: z.enum(["unverified", "confirmed"]).default("unverified"), sourceKind, sourceRef: z.string().trim().min(3).max(500) }).strict(), z.object({ kind: z.literal("confirm_facts"), keys: z.array(key).min(1).max(100).refine((v) => new Set(v).size === v.length) }).strict(), z.object({ kind: z.literal("bind_fabric"), fabricCode: z.string().regex(/^[\w-]{1,40}$/), sides: z.array(z.enum(["L", "P", "S"])).min(1).max(2).refine((v) => new Set(v).size === v.length) }).strict(), z.object({ kind: z.literal("compose_offer"), channel: z.enum(["all", "main", "allegro", "woocommerce", "google", "meta"]).default("all") }).strict(), z.object({ kind: z.literal("recolor_missing"), roles: z.array(z.enum(photoRoles)).min(1).max(8).default([...photoRoles]), variantIds: z.array(id).min(1).max(500).optional() }).strict(), z.object({ kind: z.literal("review_media") }).strict(), z.object({ kind: z.literal("render_card") }).strict(), z.object({ kind: z.literal("generate_shot"), role: z.enum(photoRoles) }).strict(), z.object({ kind: z.literal("export_base") }).strict(), ]); export type OrchestratorAction = z.infer; export type ActionRequest = z.input; export const prepareOperationSchema = z.object({ action: actionSchema, idempotencyKey: z.string().regex(/^[\w:-]{1,120}$/), directive: z.string().trim().max(1000).optional() }).strict(); export const operationDecisionSchema = z.object({ decision: z.enum(["approve", "reject"]), expectedPreviewSha256: sha, note: z.string().trim().min(3).max(1000) }).strict(); export const directiveSchema = z.object({ text: z.string().trim().min(3).max(1000) }).strict(); export const stageSchema = z.enum(["materialy", "fakty", "galeria", "warianty", "dokumenty", "oferta"]); export const previewSchema = z.object({ title: z.string(), summary: z.string(), requiredRole: z.enum(["operator", "approver"]), cost: z.object({ externalUsd: z.number().nonnegative().nullable(), description: z.string() }), blockers: z.array(z.string()), warnings: z.array(z.string()), changes: z.array(z.object({ label: z.string(), before: z.json(), after: z.json() })), items: z.array(z.object({ id: z.string(), label: z.string(), sha256: sha.nullable() })), effects: z.array(z.string()), stage: stageSchema, }).strict(); export type OperationPreview = z.infer; export const operationResultSchema = z.object({ revisionId: z.string().optional(), assetIds: z.array(z.string()).optional(), verdictIds: z.array(z.string()).optional(), factsChanged: z.number().optional(), variantsCreated: z.number().optional(), completed: z.number().optional(), total: z.number().optional(), }).strict(); export type OperationResult = z.infer; export const operationSchema = z.object({ id: id, productId: id, action: actionSchema, preview: previewSchema, previewSha256: sha, status: z.enum(["proposed", "queued", "running", "succeeded", "rejected", "stale", "failed"]), createdBy: id, createdAt: z.string(), expiresAt: z.string(), decidedBy: id.nullable(), decidedAt: z.string().nullable(), decisionNote: z.string().nullable(), jobId: id.nullable(), result: operationResultSchema.nullable(), error: z.string().nullable(), directive: z.string().nullable(), }).strict(); export type Operation = z.infer; export const operationSummarySchema = operationSchema.omit({ action: true, preview: true, directive: true }).extend({ title: z.string(), stage: stageSchema }); export type OperationSummary = z.infer; export const recommendationSchema = z.object({ id: z.string(), priority: z.enum(["blocker", "next", "review"]), area: z.enum(["facts", "fabrics", "gallery", "variants", "documents", "content", "integrations", "quality"]), title: z.string(), reason: z.string(), action: actionSchema.nullable(), stage: stageSchema, cta: z.string(), }); export type Recommendation = z.infer; export const inspectionSchema = z.object({ productId: id, version: z.literal(ORCHESTRATOR_VERSION), stateSha256: sha, checkedAt: z.string(), recommendations: z.array(recommendationSchema), counts: z.object({ pendingFacts: z.number(), variants: z.number(), approvedMasters: z.number(), missingMedia: z.number(), candidates: z.number(), qualityIssues: z.number() }), boundaries: z.array(z.string()), }).strict(); export type ProductInspection = z.infer; export interface DirectiveResult { action: ActionRequest | null; message: string; examples: string[] }