Unified · Split
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | import { |
| 2 | + isProviderSendTurnSupportedImageMimeType, | |
| 2 | 3 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, |
| 3 | 4 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, |
| 4 | 5 | } from "@t3tools/contracts"; |
| @@ -162,6 +163,13 @@ export async function buildIncomingShareDraft(input: { · 157 unmodified lines | ||
| 162 | 163 | await releaseOwnedFiles(input.fileReader, [uri, payload.value]); |
| 163 | 164 | continue; |
| 164 | 165 | } |
| 166 | + if (!isProviderSendTurnSupportedImageMimeType(mimeType)) { | |
| 167 | + warnings.push( | |
| 168 | + `'${resolved?.originalName ?? fallbackName(uri, index, mimeType)}' is not a supported image type.`, | |
| 169 | + ); | |
| 170 | + await releaseOwnedFiles(input.fileReader, [uri, payload.value]); | |
| 171 | + continue; | |
| 172 | + } | |
| 165 | 173 | if ( |
| 166 | 174 | resolved?.contentSize !== null && |
| 167 | 175 | resolved?.contentSize !== undefined && |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | import { |
| 2 | + isProviderSendTurnSupportedImageMimeType, | |
| 2 | 3 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, |
| 3 | 4 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, |
| 4 | 5 | type UploadChatImageAttachment, |
| @@ -98,6 +99,10 @@ export async function pickComposerImages(input: { readonly existingCount: number · 93 unmodified lines | ||
| 98 | 99 | error = `Unsupported file type for '${asset.fileName ?? "image"}'.`; |
| 99 | 100 | continue; |
| 100 | 101 | } |
| 102 | + if (!isProviderSendTurnSupportedImageMimeType(mimeType)) { | |
| 103 | + error = `'${asset.fileName ?? "image"}' is not a supported image type. Attach GIF, JPEG, PNG, or WebP images.`; | |
| 104 | + continue; | |
| 105 | + } | |
| 101 | 106 | |
| 102 | 107 | const base64 = asset.base64; |
| 103 | 108 | if (!base64) { |
| @@ -12,6 +12,7 @@ import type { · 11 unmodified lines | ||
| 12 | 12 | ThreadId, |
| 13 | 13 | } from "@t3tools/contracts"; |
| 14 | 14 | import { |
| 15 | + isProviderSendTurnSupportedImageMimeType, | |
| 15 | 16 | ProviderDriverKind, |
| 16 | 17 | ProviderInstanceId, |
| 17 | 18 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, |
| @@ -2305,6 +2306,10 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) · 2287 unmodified lines | ||
| 2305 | 2306 | error = `Unsupported file type for '${file.name}'. Please attach image files only.`; |
| 2306 | 2307 | continue; |
| 2307 | 2308 | } |
| 2309 | + if (!isProviderSendTurnSupportedImageMimeType(file.type)) { | |
| 2310 | + error = `'${file.name}' is not a supported image type. Attach GIF, JPEG, PNG, or WebP images.`; | |
| 2311 | + continue; | |
| 2312 | + } | |
| 2308 | 2313 | if (reservedCount >= PROVIDER_SEND_TURN_MAX_ATTACHMENTS) { |
| 2309 | 2314 | error = `You can attach up to ${PROVIDER_SEND_TURN_MAX_ATTACHMENTS} images per message.`; |
| 2310 | 2315 | break; |
| @@ -23,6 +23,7 @@ import { · 22 unmodified lines | ||
| 23 | 23 | ThreadCreatedPayload, |
| 24 | 24 | ThreadTurnDiff, |
| 25 | 25 | ThreadTurnStartRequestedPayload, |
| 26 | + isProviderSendTurnSupportedImageMimeType, | |
| 26 | 27 | } from "./orchestration.ts"; |
| 27 | 28 | import { ProviderInstanceId } from "./providerInstance.ts"; |
| 28 | 29 | |
| @@ -935,3 +936,9 @@ it.effect("project favicon overrides accept only supported image files", () => · 906 unmodified lines | ||
| 935 | 936 | assert.strictEqual(invalid._tag, "Failure"); |
| 936 | 937 | }), |
| 937 | 938 | ); |
| 939 | + | |
| 940 | +it("isProviderSendTurnSupportedImageMimeType accepts raster formats and rejects svg", () => { | |
| 941 | + assert.strictEqual(isProviderSendTurnSupportedImageMimeType("image/png"), true); | |
| 942 | + assert.strictEqual(isProviderSendTurnSupportedImageMimeType("IMAGE/JPEG"), true); | |
| 943 | + assert.strictEqual(isProviderSendTurnSupportedImageMimeType("image/svg+xml"), false); | |
| 944 | +}); | |
| @@ -144,6 +144,20 @@ export type ProviderUserInputAnswers = typeof ProviderUserInputAnswers.Type; · 143 unmodified lines | ||
| 144 | 144 | export const PROVIDER_SEND_TURN_MAX_INPUT_CHARS = 120_000; |
| 145 | 145 | export const PROVIDER_SEND_TURN_MAX_ATTACHMENTS = 8; |
| 146 | 146 | export const PROVIDER_SEND_TURN_MAX_IMAGE_BYTES = 10 * 1024 * 1024; |
| 147 | +export const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES = [ | |
| 148 | + "image/gif", | |
| 149 | + "image/jpeg", | |
| 150 | + "image/png", | |
| 151 | + "image/webp", | |
| 152 | +] as const; | |
| 153 | +const PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET = new Set<string>( | |
| 154 | + PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPES, | |
| 155 | +); | |
| 156 | + | |
| 157 | +/** Whether a pasted or picked image mime type can be sent on a provider turn. */ | |
| 158 | +export function isProviderSendTurnSupportedImageMimeType(mimeType: string): boolean { | |
| 159 | + return PROVIDER_SEND_TURN_SUPPORTED_IMAGE_MIME_TYPE_SET.has(mimeType.toLowerCase()); | |
| 160 | +} | |
| 147 | 161 | const PROVIDER_SEND_TURN_MAX_IMAGE_DATA_URL_CHARS = 14_000_000; |
| 148 | 162 | const CHAT_ATTACHMENT_ID_MAX_CHARS = 128; |
| 149 | 163 | // Correlation id is command id by design in this model. |