diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 0b44c7ad5a79..7e14fa59a2a5 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -150,8 +150,20 @@ export const ReadTool = Tool.defineEffect( const loaded = yield* instruction.resolve(ctx.messages, filepath, ctx.messageID) const mime = AppFileSystem.mimeType(filepath) - const isImage = mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" + const supportedImageMimes = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]) + const isImage = supportedImageMimes.has(mime) + const isUnsupportedImage = + !isImage && mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" const isPdf = mime === "application/pdf" + + if (isUnsupportedImage) { + return yield* Effect.fail( + new Error( + `Cannot read image: ${mime} is not a supported format. Supported image formats: JPEG, PNG, GIF, WebP.`, + ), + ) + } + if (isImage || isPdf) { const msg = `${isImage ? "Image" : "PDF"} read successfully` return { diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts index 12345266b318..f4ba81125845 100644 --- a/packages/opencode/test/tool/read.test.ts +++ b/packages/opencode/test/tool/read.test.ts @@ -425,6 +425,39 @@ root_type Monster;` expect(result.output).toContain("table Monster") }), ) + + it.live("rejects unsupported image formats like BMP", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + // minimal BMP file header + const bmp = Buffer.from("BM", "ascii") + yield* put(path.join(dir, "image.bmp"), bmp) + + const err = yield* fail(dir, { filePath: path.join(dir, "image.bmp") }) + expect(err.message).toContain("image/bmp is not a supported format") + expect(err.message).toContain("JPEG, PNG, GIF, WebP") + }), + ) + + it.live("rejects unsupported image formats like TIFF", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "photo.tiff"), Buffer.from("II", "ascii")) + + const err = yield* fail(dir, { filePath: path.join(dir, "photo.tiff") }) + expect(err.message).toContain("is not a supported format") + }), + ) + + it.live("rejects unsupported image formats like AVIF", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "photo.avif"), Buffer.from([0x00, 0x00, 0x00, 0x1c])) + + const err = yield* fail(dir, { filePath: path.join(dir, "photo.avif") }) + expect(err.message).toContain("is not a supported format") + }), + ) }) describe("tool.read loaded instructions", () => {