@sema-agent/core
Version:
Stateless, task-oriented AI agent core
37 lines (36 loc) • 1.84 kB
JavaScript
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 };
}