feat(durable): read returns images, optional Photon image processor

read returns an image file as one image block (programs get an ImageContent) plus an info diagnostic,
instead of an unsupported_image error. Limits are the conversation model's inputLimits.images.resize,
by default 2000x2000 pixels and 4.5 MB of base64.

- Without a processor: PNG, JPEG, GIF, and WebP within the limits pass through undecoded; the byte limit
  is checked before reading the file, dimensions from the header. Larger images and BMP are error results.
- With createCodingTools({ images }): the processor orients by EXIF, converts BMP to PNG, and shrinks to
  fit. ImageProcessor.prepare is async.
- @earendil-works/pi-durable/images: createPhotonImages(wasm) on @silvia-odwyer/photon 0.3.3 (web build);
  /images/node and /images/cloudflare load the wasm. The root and ./tools entries never reach it (enforced
  in check-entry-graphs).
- An image must be read whole: one that changes while read is read again, unlike a growing text file.
- A model without image input gets a diagnostic that it sees a placeholder.
This commit is contained in:
Mario Zechner 2026-10-09 19:38:02 +00:00
parent eba8497395
commit 42a3497d03
20 changed files with 1099 additions and 64 deletions

View file

@ -46,8 +46,15 @@ const BUDGETS = {
// Tool argument validation reaches TypeBox; provider-session creation reaches pi-ai's lean UUID utility; the
// memory and SQLite storages share scan order and cursor handling.
maxFiles: 63,
forbid: ["packages/ai/src/index.ts", "packages/ai/src/utils/typebox-helpers.ts"],
forbid: [
"packages/ai/src/index.ts",
"packages/ai/src/utils/typebox-helpers.ts",
// Photon (WebAssembly) loads only through `./images`.
"packages/durable/src/images/",
],
},
// The file tools reach their environment helpers and the Harness definitions they need, not the Harness itself.
"./tools": { maxFiles: 17, forbid: ["packages/durable/src/images/"] },
},
};