import { z } from "zod"; const boundedText = z.string().max(12_000); const timestamp = z.number().int().nonnegative().max(8_640_000_000_000_000); const referenceSchema = z.object({ id: z.string().max(200), title: z.string().max(256), url: z.string().max(4096), type: z.enum(["image", "video"]), duration: z.number().optional(), }); const nodeContextSchema = z.object({ id: z.string().max(200), title: z.string().max(256), type: z.string().max(100), description: boundedText, }); const shotSchema = z.object({ shot: boundedText.default(""), duration: boundedText.default(""), scene: boundedText.default(""), sceneDescription: boundedText, shotSize: boundedText.default(""), lighting: boundedText.default(""), dialogue: boundedText.default(""), backgroundMusic: boundedText.default(""), voiceAndSfx: boundedText.default(""), cameraMovement: boundedText.default(""), imagePrompt: boundedText.default(""), motionPrompt: boundedText.default(""), keyframeUrl: z.string().max(4096).default(""), }); export const tvstarSessionSchema = z.object({ id: z.string().uuid(), title: z.string().min(1).max(256), goal: boundedText, mode: z.enum(["full", "brainstorm", "script", "storyboard", "edit"]), plan: z.object({ summary: boundedText, agentPlan: z.array(boundedText).max(30), deliverables: z.array(boundedText).max(30), recommendedNodes: z.array(boundedText).max(30), model: z.string().max(256).optional(), }).optional(), shots: z.array(shotSchema).max(12).optional(), references: z.array(referenceSchema).max(6).optional(), nodeContexts: z.array(nodeContextSchema).max(6).optional(), messages: z.array(z.object({ id: z.string().max(200), role: z.enum(["user", "assistant"]), content: boundedText, createdAt: timestamp, })).max(500).optional(), imageModelRef: z.string().max(256).optional(), videoModelRef: z.string().max(256).optional(), autoGenerate: z.boolean().optional(), createdAt: timestamp, updatedAt: timestamp, pendingStage: z.enum(["plan", "shots", "submit"]).optional(), runSubmitted: z.boolean().optional(), preparedOnly: z.boolean().optional(), groupId: z.string().max(200).optional(), }); export type StoredTVstarSession = z.infer; export function mergeTVstarSessions( local: T[], remote: T[] ): T[] { const byId = new Map(); for (const item of [...remote, ...local]) { const previous = byId.get(item.id); if (!previous || (item.updatedAt || item.createdAt) >= (previous.updatedAt || previous.createdAt)) { byId.set(item.id, item); } } return [...byId.values()].sort((a, b) => (b.updatedAt || b.createdAt) - (a.updatedAt || a.createdAt)).slice(0, 20); } export function publicTVstarSnapshot(session: StoredTVstarSession) { return { title: session.title, goal: session.goal, createdAt: session.createdAt, messages: (session.messages || []).map(({ role, content, createdAt }) => ({ role, content, createdAt })), plan: session.plan ? { summary: session.plan.summary, deliverables: session.plan.deliverables } : null, shots: (session.shots || []).map(({ shot, duration, sceneDescription }) => ({ shot, duration, sceneDescription })), }; }