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>
51 lines
1.4 KiB
TypeScript
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;
|
|
}
|