import { z } from "zod"; export const CreateConversationDto = z.object({ title: z.string().max(200).optional(), context: z .object({ pageId: z.string().min(1).optional(), databaseId: z.string().min(1).optional(), recordId: z.string().min(1).optional(), payload: z.record(z.unknown()).optional() }) .default({}) }); export type CreateConversationInput = z.infer; export const ChatDto = z.object({ conversationId: z.string().min(1).optional(), message: z.string().min(1).max(32_000), context: z .object({ pageId: z.string().min(1).optional(), databaseId: z.string().min(1).optional(), recordId: z.string().min(1).optional(), payload: z.record(z.unknown()).optional() }) .optional() }); export type ChatInput = z.infer; export const ListMessagesQuery = z.object({ cursor: z.string().optional(), limit: z.string().optional() }); export type ListMessagesInput = z.infer; export const RenameConversationDto = z.object({ title: z.string().min(1).max(200) }); export type RenameConversationInput = z.infer; /** * TASK-0122 — the SSE message-send body. `context` carries RESOURCE IDS ONLY * (the server assembles the actual content — the client never sends raw page * text for context binding). `retryOf` re-sends an already-persisted user * message without duplicating its row (the error-banner Retry button). */ export const AiContextDto = z .object({ pageId: z.string().min(1).optional(), blockId: z.string().min(1).optional(), databaseId: z.string().min(1).optional() }) .refine((c) => c.pageId || c.blockId || c.databaseId, { message: "context needs at least one of pageId/blockId/databaseId" }); export const SendMessageDto = z.object({ content: z.string().min(1).max(32_000), context: AiContextDto.optional(), retryOf: z.string().min(1).optional() }); export type SendMessageInput = z.infer;