Files
fusion/packages/core/src/image-mime.ts
gsxdsm 8eed09c126 FN-7211: fix image attachment MIME sniffing
Correct image attachment media types from bytes before sending image blocks to models.

- Add core image MIME sniffing for PNG, JPEG, GIF, and WEBP signatures.
- Use detected image bytes in task triage and dashboard chat attachment image blocks while preserving stored attachment metadata.
- Cover mismatched and unknown image bytes with core, triage, and chat attachment tests.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7211-webp-image-mime-sniff.md        |  7 +++
 packages/core/src/__tests__/image-mime.test.ts     | 29 ++++++++++
 packages/core/src/image-mime.ts                    | 50 +++++++++++++++++
 packages/core/src/index.ts                         |  2 +
 .../src/__tests__/chat-attachment-content.test.ts  | 54 +++++++++++++++++--
 packages/dashboard/src/chat-attachment-content.ts  |  9 +++-
 packages/engine/src/__tests__/triage.test.ts       | 63 +++++++++++++++++-----
 packages/engine/src/triage.ts                      |  8 ++-
 8 files changed, 201 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-7211

Fusion-Task-Lineage: 22bfb812-15c4-4ff7-a281-87b25427561e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-28 16:51:49 -07:00

51 lines
1.4 KiB
TypeScript

export type DetectedImageMime = "image/png" | "image/jpeg" | "image/gif" | "image/webp";
/*
FNXC:ImageAttachments 2026-06-28-00:00:
FN-7211: AI image-block media_type must match the real image bytes. Attachment storage keeps the extension-derived mimeType for display, but Anthropic rejects mismatched pairings such as stored image/webp over PNG bytes, so model-bound image blocks sniff bytes at build time.
*/
export function detectImageMimeFromBytes(bytes: Buffer | Uint8Array): DetectedImageMime | null {
if (bytes.length >= 8
&& bytes[0] === 0x89
&& bytes[1] === 0x50
&& bytes[2] === 0x4e
&& bytes[3] === 0x47
&& bytes[4] === 0x0d
&& bytes[5] === 0x0a
&& bytes[6] === 0x1a
&& bytes[7] === 0x0a) {
return "image/png";
}
if (bytes.length >= 3
&& bytes[0] === 0xff
&& bytes[1] === 0xd8
&& bytes[2] === 0xff) {
return "image/jpeg";
}
if (bytes.length >= 6
&& bytes[0] === 0x47
&& bytes[1] === 0x49
&& bytes[2] === 0x46
&& bytes[3] === 0x38
&& (bytes[4] === 0x37 || bytes[4] === 0x39)
&& bytes[5] === 0x61) {
return "image/gif";
}
if (bytes.length >= 12
&& bytes[0] === 0x52
&& bytes[1] === 0x49
&& bytes[2] === 0x46
&& bytes[3] === 0x46
&& bytes[8] === 0x57
&& bytes[9] === 0x45
&& bytes[10] === 0x42
&& bytes[11] === 0x50) {
return "image/webp";
}
return null;
}