UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

37 lines (36 loc) • 1.84 kB
import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js"; import { imageMagicMatches } from "./safety.js"; import { MAX_IMAGE_READ_BYTES, MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES } from "./fs-shared.js"; export const ADMITTED_IMAGE_MIME_TYPES = Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]); export function imageReadCap(downsampler) { return downsampler ? MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES : MAX_IMAGE_READ_BYTES; } export async function admitImageBytes(bytes, claimedMime, downsampler) { const rawBytes = bytes.byteLength; if (!ADMITTED_IMAGE_MIME_TYPES.includes(claimedMime)) return { ok: false, reason: "mime_unsupported", rawBytes }; const cap = imageReadCap(downsampler); if (rawBytes > cap) return { ok: false, reason: "raw_too_large", rawBytes, cap }; if (!imageMagicMatches(bytes, claimedMime)) return { ok: false, reason: "magic_mismatch", rawBytes }; let base64; let mimeType = claimedMime; let dimensions; if (downsampler) { const out = await downsampler(Buffer.from(bytes), claimedMime).catch(() => undefined); if (out) { base64 = out.base64; mimeType = out.mimeType; dimensions = out.dimensions; } } base64 ??= Buffer.from(bytes).toString("base64"); if (base64.length > MCP_IMAGE_MAX_BASE64) return { ok: false, reason: "base64_too_large", rawBytes, base64 }; const resized = dimensions !== undefined && dimensions.displayWidth > 0 && dimensions.displayHeight > 0 && (dimensions.originalWidth !== dimensions.displayWidth || dimensions.originalHeight !== dimensions.displayHeight); return { ok: true, block: { type: "image", data: base64, mimeType }, originalSize: rawBytes, ...(dimensions !== undefined ? { dimensions } : {}), resized }; }