diff --git a/packages/provider-opencode/src/server/OpenCodeRuntime.cliParsers.test.ts b/packages/provider-opencode/src/server/OpenCodeRuntime.cliParsers.test.ts index 499d7d5fee7368c24d2e415479bd4775cf6582fc..8394d8023d7639040f956cbd820c4054b6fdf513 100644 --- a/packages/provider-opencode/src/server/OpenCodeRuntime.cliParsers.test.ts +++ b/packages/provider-opencode/src/server/OpenCodeRuntime.cliParsers.test.ts @@ -293,11 +293,15 @@ describe("toOpenCodeFileParts", () => { const parts = OpenCodeRuntime.toOpenCodeFileParts({ attachments: [ attachment("application/pdf"), - attachment("text/markdown"), + attachment("text/plain"), attachment("image/png"), // A ZIP file part makes OpenCode's Anthropic path throw before the // turn starts; it must ride only as the prompt's file path line. attachment("application/zip"), + // OpenCode inlines only text/plain itself. Other text types reach the + // model API as raw file parts, which Anthropic and OpenAI reject. + attachment("text/html"), + attachment("text/markdown"), attachment("application/octet-stream"), // Image formats the model APIs reject stay on the fallback path too. attachment("image/bmp"), @@ -310,7 +314,7 @@ describe("toOpenCodeFileParts", () => { NodeAssert.deepEqual( parts.map((part) => part.mime), - ["application/pdf", "text/markdown", "image/png"], + ["application/pdf", "text/plain", "image/png"], ); }); diff --git a/packages/provider-opencode/src/server/OpenCodeRuntime.ts b/packages/provider-opencode/src/server/OpenCodeRuntime.ts index b6651d48a721b975db526ef34cdf8a6983a2a43f..d88a0321867479c7424a7983388b929e8c21beb7 100644 --- a/packages/provider-opencode/src/server/OpenCodeRuntime.ts +++ b/packages/provider-opencode/src/server/OpenCodeRuntime.ts @@ -469,7 +469,9 @@ export function openCodeQuestionId( * else (ZIP, binaries, image formats like BMP/AVIF/SVG that model APIs * reject, or files over the direct-attachment size limit) would make the turn * fail before it starts, so those ride only as the file path ProviderService - * puts in the prompt. + * puts in the prompt. Text is native only as text/plain, which OpenCode reads + * itself; other text types (HTML, Markdown, CSV) reach the model API as raw + * file parts, which Anthropic and OpenAI reject. */ const OPENCODE_NATIVE_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]); const OPENCODE_NATIVE_FILE_PART_MAX_BYTES = 20 * 1024 * 1024; @@ -484,7 +486,7 @@ function isOpenCodeNativeFilePart(input: { const normalized = input.mimeType.trim().toLowerCase(); return ( OPENCODE_NATIVE_IMAGE_MIMES.has(normalized) || - normalized.startsWith("text/") || + normalized === "text/plain" || normalized === "application/pdf" ); }