import { z } from 'zod'; import { ErrorResponseSchema, RejectedActionResponseSchema } from './common.js'; import { RedditAccountOwnerSchema } from './session.js'; export const ChatMediaSchema = z.strictObject({ originalUrl: z.string(), localPath: z.string(), fileName: z.string(), }); export const ChatMessageSchema = z.strictObject({ eventId: z.string().optional(), sender: z.string(), text: z.string(), timestamp: z.number(), isSelf: z.boolean(), isAi: z.boolean().optional(), media: z.array(ChatMediaSchema).optional(), }); export const ActiveChatSchema = z.strictObject({ username: z.string(), roomName: z.string(), lastMessageTime: z.number(), lastMessageSnippet: z.string(), isFromUs: z.boolean(), unread: z.boolean(), url: z.string().optional(), roomId: z.string().optional(), messages: z.array(ChatMessageSchema).optional(), originMessage: z.string().optional(), hasReceivedImages: z.boolean().optional(), owner: RedditAccountOwnerSchema.optional(), }); export const ChatsResponseSchema = z.array(ActiveChatSchema); export const ConversationsResponseSchema = ChatsResponseSchema; export const SyncKindSchema = z.union([z.literal('full'), z.literal('delta')]); export const SyncPhaseSchema = z.union([ z.literal('starting'), z.literal('seeding'), z.literal('listing-rooms'), z.literal('resolving-usernames'), z.literal('loading-details'), z.literal('finalizing'), z.literal('done'), ]); export const SyncProgressSchema = z.strictObject({ kind: SyncKindSchema, phase: SyncPhaseSchema, current: z.number(), total: z.number(), label: z.string().optional(), startedAt: z.number(), }); export const ChatSyncStatusSchema = z.strictObject({ syncing: z.boolean(), syncingKind: SyncKindSchema.nullable(), lastFullSyncAt: z.number().nullable(), lastDeltaSyncAt: z.number().nullable(), hasEverFullSynced: z.boolean(), cachedChats: z.number(), progress: SyncProgressSchema.nullable(), }); export const ConversationDetailResponseSchema = ActiveChatSchema.extend({ triggeringPost: z.strictObject({ id: z.string(), subreddit: z.string().optional(), title: z.string().optional(), url: z.string(), imageUrl: z.string().nullable().optional(), postedAt: z.number().optional(), sentAt: z.number(), }).nullable(), automation: z.strictObject({ turnsUsed: z.number().int().min(0).max(3), remainingTurns: z.number().int().min(0).max(3), status: z.enum(['active', 'image-received', 'human-required', 'boundary', 'user-overridden']), handoffReason: z.string().nullable(), }), syncInProgress: z.strictObject({ kind: SyncKindSchema.nullable(), progress: SyncProgressSchema.nullable(), staleMessages: z.boolean(), }).nullable(), }); export const SuggestedReplyResponseSchema = z.strictObject({ suggestedReply: z.string(), intent: z.unknown().optional(), conversationState: z.unknown().optional(), }); export const SendReplyRequestSchema = z.strictObject({ message: z.string().min(1) }); export const StartConversationRequestSchema = z.strictObject({ accountId: z.string().min(1), username: z.string().min(1), message: z.string().min(1), }); export const StartConversationResponseSchema = z.strictObject({ success: z.literal(true), chat: ActiveChatSchema, }); export const ChatSyncTriggerResponseSchema = z.union([ z.strictObject({ success: z.literal(true), kind: SyncKindSchema }), RejectedActionResponseSchema, ErrorResponseSchema, ]); export const FetchAllChatsResponseSchema = z.strictObject({ success: z.literal(true), count: z.number(), }); export type ActiveChat = z.infer; export type ChatSyncStatus = z.infer; export type ConversationDetailResponse = z.infer;