export const ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024; export const ATTACHMENT_MAX_COUNT = 4; /** Base64 expands payload by 4/3; cap before decode to reject oversize uploads cheaply. */ export const ATTACHMENT_MAX_BASE64_LENGTH = Math.ceil(ATTACHMENT_MAX_BYTES / 3) * 4; export const ATTACHMENT_IMAGE_MIME_TYPES = [ "image/jpeg", "image/png", "image/webp", "image/gif", ] as const; export const ATTACHMENT_VIDEO_MIME_TYPES = ["video/mp4", "video/webm", "video/quicktime"] as const; export const ATTACHMENT_AUDIO_MIME_TYPES = [ "audio/mpeg", "audio/mp4", "audio/wav", "audio/webm", "audio/ogg", ] as const; export const ATTACHMENT_FILE_MIME_TYPES = [ "application/pdf", "text/plain", "text/markdown", "text/csv", "application/json", "text/vtt", "application/x-subrip", ] as const; export const ATTACHMENT_ALLOWED_MIME_TYPES = [ ...ATTACHMENT_IMAGE_MIME_TYPES, ...ATTACHMENT_VIDEO_MIME_TYPES, ...ATTACHMENT_AUDIO_MIME_TYPES, ...ATTACHMENT_FILE_MIME_TYPES, ] as const; export type AttachmentKind = "image" | "video" | "audio" | "file"; /** * Size caps for artifacts that do not travel inline as base64 (streamed uploads, generated and * edited media). Inline RPC uploads stay capped by ATTACHMENT_MAX_BYTES. */ export const ARTIFACT_MAX_BYTES_BY_KIND: Record = { image: 25 * 1024 * 1024, video: 500 * 1024 * 1024, audio: 200 * 1024 * 1024, file: 25 * 1024 * 1024, }; export type AttachmentMimeType = (typeof ATTACHMENT_ALLOWED_MIME_TYPES)[number]; export function isAttachmentImageMimeType(mimeType: string): boolean { return (ATTACHMENT_IMAGE_MIME_TYPES as readonly string[]).includes(mimeType); } export function isAttachmentVideoMimeType(mimeType: string): boolean { return (ATTACHMENT_VIDEO_MIME_TYPES as readonly string[]).includes(mimeType); } export function isAttachmentAudioMimeType(mimeType: string): boolean { return (ATTACHMENT_AUDIO_MIME_TYPES as readonly string[]).includes(mimeType); } export function attachmentKindOf(mimeType: string): AttachmentKind { if (isAttachmentImageMimeType(mimeType)) return "image"; if (isAttachmentVideoMimeType(mimeType)) return "video"; if (isAttachmentAudioMimeType(mimeType)) return "audio"; return "file"; } export function isAllowedAttachmentMimeType(mimeType: string): mimeType is AttachmentMimeType { return (ATTACHMENT_ALLOWED_MIME_TYPES as readonly string[]).includes(mimeType); } export function validateThreadsSendInput(input: { text?: string; artifactIds?: string[]; }): boolean { const text = input.text?.trim() ?? ""; const artifactIds = input.artifactIds ?? []; return Boolean(text || artifactIds.length); }