diff --git a/bun.lock b/bun.lock index 44ff5fab77..3d8037e6fc 100644 --- a/bun.lock +++ b/bun.lock @@ -296,7 +296,7 @@ "dependencies": { "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/amazon-bedrock": "4.0.166", - "@ai-sdk/anthropic": "3.0.82", + "@ai-sdk/anthropic": "3.0.111", "@ai-sdk/azure": "3.0.88", "@ai-sdk/cerebras": "2.0.41", "@ai-sdk/cohere": "3.0.27", @@ -571,7 +571,7 @@ "@agentclientprotocol/sdk": "0.21.0", "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/amazon-bedrock": "4.0.166", - "@ai-sdk/anthropic": "3.0.82", + "@ai-sdk/anthropic": "3.0.111", "@ai-sdk/azure": "3.0.88", "@ai-sdk/cerebras": "2.0.60", "@ai-sdk/cohere": "3.0.27", @@ -1066,10 +1066,12 @@ "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", + "@ai-sdk/anthropic@3.0.111": "patches/@ai-sdk%2Fanthropic@3.0.111.patch", "@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", - "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", + "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", + "@ai-sdk/amazon-bedrock@4.0.166": "patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch", "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", @@ -1077,6 +1079,7 @@ "@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch", }, "overrides": { + "@ai-sdk/anthropic": "3.0.111", "@opentui/core": "catalog:", "@opentui/keymap": "catalog:", "@opentui/solid": "catalog:", @@ -1170,7 +1173,7 @@ "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.166", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.115", "@ai-sdk/openai": "3.0.105", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.50", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HzzQb+ks+WmRcnnT9uBdJAxaqSXz3CWgmLSe9hePAkELPzTl/nAIJh2fb2+UsTD0n9OzZfBb25k68CIxraKkoA=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.82", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-WKKou2wbhGGYV8PSALAPyV2YY4nfCqCPkyBzYtJtDA9yCcIFwsbtkTNgg7bqtLCVzeEsY7wwxRoCWy+EMfrw/A=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.111", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.46" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-atgBW8jZPr/KuaKX5FvDIHuXBI8VCol6kVeoD4P0657+VXR73QsLogXQVN/Zt5FHtq9WzpdIZseCJiXPqkgwwA=="], "@ai-sdk/azure": ["@ai-sdk/azure@3.0.88", "", { "dependencies": { "@ai-sdk/deepseek": "2.0.47", "@ai-sdk/openai": "3.0.84", "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RRjZkB1lYplh8dpBarnvkl1j7sYLHsyXua7erL3oNcMK7fHcl4bPO5C7iQhD1O/DqD/zCceDifnege1s+8yEvw=="], @@ -5626,8 +5629,6 @@ "@ai-sdk/alibaba/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], - "@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.115", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.50" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-S1oAUCVaB2Fr6LtMDfa9EmVlDVczpRodYc3IKdLD2z4r0s/pX+zwjqlhiwhS1FsCS2jVx8EnSyBtuPuOzF094A=="], - "@ai-sdk/amazon-bedrock/@ai-sdk/openai": ["@ai-sdk/openai@3.0.105", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.50" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-umx95F8gqGuhPdoW+4ofq83fuSN6aMHwISxvY6dZU51Iix5ZKId0FNru+0FQeu+xfFUrVNmf3vYQNc2FLN85xA=="], "@ai-sdk/amazon-bedrock/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], @@ -5638,9 +5639,9 @@ "@ai-sdk/amazon-bedrock/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], - "@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="], + "@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], - "@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="], + "@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.46", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tEtld97plCFiYevsJuOkGkeuhQndeMWFBVrJS4AjnbD5AqrNSXRCe0p+BZ3Cju/sxDeeZ9ym3q9YUV8fASA7aQ=="], "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], @@ -5676,8 +5677,6 @@ "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="], - "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="], - "@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.108", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kwvYpRNghqt0VRKE7Hx1UWZQCUJJFqUITj24baxy+ApS0Hru0PkBJHD75a36Wc+e6e+wHcKR2MconTeJiBZigA=="], "@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.67", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-glcEJC2mBXJKj7joFI0fRhcbdDYKTBgXMPcT6Vcnlym67tTzuNG9pFx3zblxVv8TdOxhojJja5zGG19yeGJxuA=="], @@ -6160,8 +6159,6 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.153", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.110", "@ai-sdk/openai": "3.0.96", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iEXrLgWylCHJmznqlKLU3CqRh8UWibv+illrwmsk136FVBBvyXiGnpQrI1pGWCScVLQjBQSFQu7GJDkUEomf/A=="], - "ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="], - "ai-gateway-provider/@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.60", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.54", "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rnok3cThg6awBwaDSyiZpgRpbV7pqxGYrA89LODCo5cuEHeP2h0AM0lLHP7zIkclAdXfOm4wldKi/S2T/DGCOw=="], "ai-gateway-provider/@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cXLjIsSzUriPHe704IH6d+ipJ/OvczTB700p9Zma7DPgQzvxG/diyr8q/2LEsbTRiTopiKhky8dn1PJNQcJToQ=="], @@ -6576,6 +6573,8 @@ "@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@ai-sdk/anthropic/@ai-sdk/provider-utils/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], + "@ai-sdk/azure/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -6984,10 +6983,6 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], - - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="], - "ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OyXt0zK8y2/ZIyWlbxTv2r1M7AK227S+Gl4BYOEF42q0wz1n5m4fwR8L4Fy/MQ4Ho6xje47MPsFcRdIqIyP6Rw=="], "ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider": ["@ai-sdk/provider@3.0.12", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-sj9DWTJ2Ze0WR9qsiOPqoqzNx3OxL6iMxHImbhvoe9qOspekbzxNDMiJ4TIGfYHYh9w4OmBjz3prvqhzTi96+Q=="], @@ -7434,10 +7429,6 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], - "ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], diff --git a/package.json b/package.json index 07144ccbde..dc4a813c50 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "electron" ], "overrides": { + "@ai-sdk/anthropic": "3.0.111", "@opentui/core": "catalog:", "@opentui/keymap": "catalog:", "@opentui/solid": "catalog:", @@ -160,6 +161,8 @@ "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", "@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch", "@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch", - "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch" + "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch", + "@ai-sdk/anthropic@3.0.111": "patches/@ai-sdk%2Fanthropic@3.0.111.patch", + "@ai-sdk/amazon-bedrock@4.0.166": "patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch" } } diff --git a/packages/core/package.json b/packages/core/package.json index ffd49ce5be..7ba2346dd1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -63,7 +63,7 @@ "dependencies": { "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/amazon-bedrock": "4.0.166", - "@ai-sdk/anthropic": "3.0.82", + "@ai-sdk/anthropic": "3.0.111", "@ai-sdk/azure": "3.0.88", "@ai-sdk/cerebras": "2.0.41", "@ai-sdk/cohere": "3.0.27", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index ac50498dee..d81bbebae5 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -57,7 +57,7 @@ "@agentclientprotocol/sdk": "0.21.0", "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/amazon-bedrock": "4.0.166", - "@ai-sdk/anthropic": "3.0.82", + "@ai-sdk/anthropic": "3.0.111", "@ai-sdk/azure": "3.0.88", "@ai-sdk/cerebras": "2.0.60", "@ai-sdk/cohere": "3.0.27", diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 28a5beb9ab..1244963e84 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -684,6 +684,41 @@ function anthropicOmitsThinking(apiId: string) { return anthropicUsesModernAdaptiveThinking(apiId) } +// Opus 5, Sonnet 5, Fable 5.x, and Mythos 5.x think without a `thinking` parameter. +function anthropicThinksByDefault(apiId: string) { + const version = /claude-(?:[a-z]+-)?(\d+)(?:[.-](\d{1,2}))?(?:[.@-]|$)/i.exec(apiId) + if (!version) return false + return Number(version[1]) >= 5 +} + +// Fable 5.1 binds each thinking signature to the system prompt, tool list, and +// messages above it, and rejects the request when any of that changes. opencode +// re-renders parts of that prefix between turns (system prompt, tools, compaction), +// so ask the API to drop the affected blocks instead of failing the request. +// Models that do not run the check accept the field, so it is safe on every Claude. +// The patched AI SDK adds the thinking-binding-controls beta whenever it is set. +const ANTHROPIC_BLOCK_BINDING = { prefixMismatchBehavior: "drop_block" } + +function anthropicBlockBinding(model: Provider.Model, options: { [x: string]: any }) { + if (!model.api.id.toLowerCase().includes("claude")) return options + const byDefault = anthropicThinksByDefault(model.api.id) + switch (model.api.npm) { + case "@ai-sdk/anthropic": + case "@ai-sdk/google-vertex/anthropic": { + const thinking = options.thinking ?? (byDefault ? { type: "adaptive" } : undefined) + if (!thinking || (thinking.type !== "adaptive" && thinking.type !== "enabled")) return options + return { ...options, thinking: { ...thinking, blockBinding: ANTHROPIC_BLOCK_BINDING } } + } + case "@ai-sdk/amazon-bedrock": { + const reasoningConfig = options.reasoningConfig ?? (byDefault ? { type: "adaptive" } : undefined) + if (!reasoningConfig || (reasoningConfig.type !== "adaptive" && reasoningConfig.type !== "enabled")) + return options + return { ...options, reasoningConfig: { ...reasoningConfig, blockBinding: ANTHROPIC_BLOCK_BINDING } } + } + } + return options +} + function googleThinkingLevelEfforts(apiId: string) { const id = apiId.toLowerCase() if (!id.includes("gemini-3")) return ["low", "high"] @@ -1363,7 +1398,7 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a usesOpenAIReasoningGate && (model.capabilities.reasoning || options.reasoningEffort !== undefined || options.reasoningSummary !== undefined) ? { ...options, forceReasoning: true } - : options + : anthropicBlockBinding(model, options) if (model.api.npm === "@ai-sdk/gateway") { // Gateway providerOptions are split across two namespaces: diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 20aa8a8404..9f8530929c 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -435,6 +435,20 @@ const layer = Layer.effect( case "step-finish": { const completedSnapshot = yield* snapshot.track() yield* Effect.forEach(Object.keys(ctx.reasoningMap), finishReasoning) + // Anthropic reports thinking blocks it removed before the model saw the + // prompt. Prefix mismatches mean opencode changed history behind a signed + // block; log them so the churn can be tracked down. + const dropped = isRecord(value.providerMetadata?.anthropic) + ? value.providerMetadata.anthropic.inputTransformations + : undefined + if (Array.isArray(dropped) && dropped.length > 0) { + yield* Effect.logWarning("thinking blocks dropped by provider", { + sessionID: ctx.sessionID, + messageID: ctx.assistantMessage.id, + model: ctx.model.id, + transformations: JSON.stringify(dropped), + }) + } const usage = Session.getUsage({ model: ctx.model, usage: value.usage ?? new Usage({}), diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 9245e3a57d..ed85f6920a 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -7,6 +7,8 @@ import { ModelV2 } from "@opencode-ai/core/model" import { ModelsDev } from "@opencode-ai/core/models-dev" import { generateText, jsonSchema, type ModelMessage } from "ai" import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock" +import { createAnthropic } from "@ai-sdk/anthropic" +import { createVertexAnthropic } from "@ai-sdk/google-vertex/anthropic" describe("ProviderTransform.options - setCacheKey", () => { const sessionID = "test-session-123" @@ -845,6 +847,199 @@ describe("ProviderTransform.providerOptions", () => { }) }) + describe("anthropic thinking block binding", () => { + const binding = { prefixMismatchBehavior: "drop_block" } + const claude = (npm: string, id: string) => + createModel({ providerID: "custom", api: { id, url: "https://example.com", npm } }) + + test("adds blockBinding to explicit adaptive thinking on @ai-sdk/anthropic", () => { + const model = claude("@ai-sdk/anthropic", "claude-opus-4-7") + expect(ProviderTransform.providerOptions(model, { thinking: { type: "adaptive" }, effort: "high" })).toEqual({ + anthropic: { thinking: { type: "adaptive", blockBinding: binding }, effort: "high" }, + }) + }) + + test("adds blockBinding to explicit enabled thinking", () => { + const model = claude("@ai-sdk/anthropic", "claude-sonnet-4-5") + expect(ProviderTransform.providerOptions(model, { thinking: { type: "enabled", budgetTokens: 4000 } })).toEqual({ + anthropic: { thinking: { type: "enabled", budgetTokens: 4000, blockBinding: binding } }, + }) + }) + + test("injects adaptive thinking for models that think by default when no variant is set", () => { + for (const id of ["claude-fable-5-1", "claude-mythos-5-1", "claude-opus-5", "claude-sonnet-5"]) { + const model = claude("@ai-sdk/anthropic", id) + expect(ProviderTransform.providerOptions(model, {})).toEqual({ + anthropic: { thinking: { type: "adaptive", blockBinding: binding } }, + }) + } + }) + + test("does not inject thinking for models that are off by default", () => { + for (const id of ["claude-opus-4-7", "claude-opus-4-5", "claude-sonnet-4-6", "claude-haiku-4-5"]) { + const model = claude("@ai-sdk/anthropic", id) + expect(ProviderTransform.providerOptions(model, {})).toEqual({ anthropic: {} }) + } + }) + + test("leaves disabled thinking alone", () => { + const model = claude("@ai-sdk/anthropic", "claude-sonnet-5") + expect(ProviderTransform.providerOptions(model, { thinking: { type: "disabled" } })).toEqual({ + anthropic: { thinking: { type: "disabled" } }, + }) + }) + + test("applies to vertex anthropic", () => { + const model = claude("@ai-sdk/google-vertex/anthropic", "claude-fable-5-1") + expect(ProviderTransform.providerOptions(model, { thinking: { type: "adaptive" }, effort: "max" })).toEqual({ + anthropic: { thinking: { type: "adaptive", blockBinding: binding }, effort: "max" }, + }) + }) + + test("applies to bedrock reasoningConfig", () => { + const model = claude("@ai-sdk/amazon-bedrock", "us.anthropic.claude-fable-5-1-v1:0") + expect( + ProviderTransform.providerOptions(model, { reasoningConfig: { type: "adaptive", maxReasoningEffort: "high" } }), + ).toEqual({ + bedrock: { reasoningConfig: { type: "adaptive", maxReasoningEffort: "high", blockBinding: binding } }, + }) + expect(ProviderTransform.providerOptions(model, {})).toEqual({ + bedrock: { reasoningConfig: { type: "adaptive", blockBinding: binding } }, + }) + }) + + test("does not touch bedrock non-anthropic models", () => { + const model = claude("@ai-sdk/amazon-bedrock", "amazon.nova-pro-v1:0") + expect(ProviderTransform.providerOptions(model, { reasoningConfig: { type: "enabled" } })).toEqual({ + bedrock: { reasoningConfig: { type: "enabled" } }, + }) + }) + + test("does not touch non-claude models on anthropic-compatible transports", () => { + const model = claude("@ai-sdk/anthropic", "kimi-k2-thinking") + expect(ProviderTransform.providerOptions(model, { thinking: { type: "adaptive" } })).toEqual({ + anthropic: { thinking: { type: "adaptive" } }, + }) + }) + + test("reaches the anthropic wire as block_binding plus beta header", async () => { + const model = claude("@ai-sdk/anthropic", "claude-fable-5-1") + let sent: { headers: Headers; body: any } | undefined + const provider = createAnthropic({ + apiKey: "test-key", + fetch: Object.assign( + async (...args: Parameters) => { + sent = { headers: new Headers(args[1]?.headers), body: JSON.parse(String(args[1]?.body)) } + return Response.json({ + type: "message", + id: "msg_1", + model: "claude-fable-5-1", + role: "assistant", + content: [{ type: "text", text: "ok" }], + stop_reason: "end_turn", + usage: { input_tokens: 1, output_tokens: 1 }, + input_transformations: [ + { type: "thinking_dropped", path: "messages.1.content.0", reason: "prefix_binding_mismatch" }, + ], + }) + }, + { preconnect: () => undefined }, + ), + }) + const result = await generateText({ + model: provider("claude-fable-5-1"), + prompt: "hi", + providerOptions: ProviderTransform.providerOptions(model, {}), + }) + expect(sent?.body.thinking).toEqual({ + type: "adaptive", + block_binding: { prefix_mismatch_behavior: "drop_block" }, + }) + expect(sent?.headers.get("anthropic-beta")?.split(",")).toContain("thinking-binding-controls-2026-08-01") + expect(result.providerMetadata?.anthropic?.inputTransformations).toEqual([ + { type: "thinking_dropped", path: "messages.1.content.0", reason: "prefix_binding_mismatch" }, + ]) + }) + + test("reaches the vertex anthropic wire as block_binding plus beta header", async () => { + const model = claude("@ai-sdk/google-vertex/anthropic", "claude-fable-5-1") + let sent: { url: string; headers: Headers; body: any } | undefined + const provider = createVertexAnthropic({ + project: "test-project", + location: "global", + generateAuthToken: async () => "test-token", + fetch: Object.assign( + async (...args: Parameters) => { + sent = { + url: String(args[0]), + headers: new Headers(args[1]?.headers), + body: JSON.parse(String(args[1]?.body)), + } + return Response.json({ + type: "message", + id: "msg_1", + model: "claude-fable-5-1", + role: "assistant", + content: [{ type: "text", text: "ok" }], + stop_reason: "end_turn", + usage: { input_tokens: 1, output_tokens: 1 }, + }) + }, + { preconnect: () => undefined }, + ), + }) + await generateText({ + model: provider("claude-fable-5-1"), + prompt: "hi", + providerOptions: ProviderTransform.providerOptions(model, {}), + }) + // Same wire shape Anthropic's own Vertex SDK produces: rawPredict URL, model moved + // out of the body, anthropic_version added, betas carried in the anthropic-beta header. + expect(sent?.url).toBe( + "https://aiplatform.googleapis.com/v1/projects/test-project/locations/global/publishers/anthropic/models/claude-fable-5-1:rawPredict", + ) + expect(sent?.body.model).toBeUndefined() + expect(sent?.body.anthropic_version).toBe("vertex-2023-10-16") + expect(sent?.body.thinking).toEqual({ + type: "adaptive", + block_binding: { prefix_mismatch_behavior: "drop_block" }, + }) + expect(sent?.headers.get("anthropic-beta")?.split(",")).toContain("thinking-binding-controls-2026-08-01") + }) + + test("reaches the bedrock wire as additionalModelRequestFields", async () => { + const model = claude("@ai-sdk/amazon-bedrock", "us.anthropic.claude-fable-5-1-v1:0") + let body: any + const provider = createAmazonBedrock({ + apiKey: "test-key", + region: "us-east-1", + fetch: Object.assign( + async (...args: Parameters) => { + body = JSON.parse(String(args[1]?.body)) + return Response.json({ + output: { message: { role: "assistant", content: [{ text: "ok" }] } }, + stopReason: "end_turn", + usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 }, + }) + }, + { preconnect: () => undefined }, + ), + }) + await generateText({ + model: provider("us.anthropic.claude-fable-5-1-v1:0"), + prompt: "hi", + providerOptions: ProviderTransform.providerOptions(model, { + reasoningConfig: { type: "adaptive", maxReasoningEffort: "high" }, + }), + }) + expect(body.additionalModelRequestFields.thinking).toEqual({ + type: "adaptive", + block_binding: { prefix_mismatch_behavior: "drop_block" }, + }) + expect(body.additionalModelRequestFields.anthropic_beta).toContain("thinking-binding-controls-2026-08-01") + }) + }) + test("forces reasoning for explicit effort even when model is not marked reasoning-capable", () => { const model = createModel({ capabilities: { diff --git a/patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch b/patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch new file mode 100644 index 0000000000..96fd15106e --- /dev/null +++ b/patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch @@ -0,0 +1,144 @@ +diff --git a/dist/index.d.mts b/dist/index.d.mts +index d194ae227fd1223ba44c8c5157b1bb72b1bab273..2e046db0a365c27659ffcec08782c7c26845c1fa 100644 +--- a/dist/index.d.mts ++++ b/dist/index.d.mts +@@ -61,6 +61,12 @@ declare const amazonBedrockLanguageModelOptions: z.ZodObject<{ + omitted: "omitted"; + summarized: "summarized"; + }>>; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>>; + anthropicBeta: z.ZodOptional>; + serviceTier: z.ZodOptional>; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>>; + anthropicBeta: z.ZodOptional>; + serviceTier: z.ZodOptional 0 || bedrockOptions.anthropicBeta) { + const existingBetas = (_g = bedrockOptions.anthropicBeta) != null ? _g : []; + const mergedBetas = betas.size > 0 ? [...existingBetas, ...Array.from(betas)] : existingBetas; +@@ -1050,7 +1057,12 @@ var BedrockChatLanguageModel = class { + ...bedrockOptions.additionalModelRequestFields, + thinking: { + type: "enabled", +- budget_tokens: thinkingBudget ++ budget_tokens: thinkingBudget, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }; + } else if (thinkingType === "adaptive") { +@@ -1058,7 +1070,12 @@ var BedrockChatLanguageModel = class { + ...bedrockOptions.additionalModelRequestFields, + thinking: { + type: "adaptive", +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }; + } +diff --git a/dist/index.mjs b/dist/index.mjs +index 5a669504cb3b21f20788956bc9c22529c1498c35..000114dfe76cba25ddfe693f042232810026c229 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -84,7 +84,10 @@ var amazonBedrockLanguageModelOptions = z.object({ + ]).optional(), + budgetTokens: z.number().optional(), + maxReasoningEffort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(), +- display: z.enum(["omitted", "summarized"]).optional() ++ display: z.enum(["omitted", "summarized"]).optional(), ++ blockBinding: z.object({ ++ prefixMismatchBehavior: z.enum(["error", "drop_block"]) ++ }).optional() + }).optional(), + /** + * Anthropic beta features to enable +@@ -1024,6 +1027,10 @@ var BedrockChatLanguageModel = class { + ...additionalTools + }; + } ++ const thinkingBlockBinding = isAnthropicModel && isThinkingEnabled ? (bedrockOptions.reasoningConfig == null ? void 0 : bedrockOptions.reasoningConfig.blockBinding) : void 0; ++ if (thinkingBlockBinding != null) { ++ betas.add("thinking-binding-controls-2026-08-01"); ++ } + if (betas.size > 0 || bedrockOptions.anthropicBeta) { + const existingBetas = (_g = bedrockOptions.anthropicBeta) != null ? _g : []; + const mergedBetas = betas.size > 0 ? [...existingBetas, ...Array.from(betas)] : existingBetas; +@@ -1054,7 +1061,12 @@ var BedrockChatLanguageModel = class { + ...bedrockOptions.additionalModelRequestFields, + thinking: { + type: "enabled", +- budget_tokens: thinkingBudget ++ budget_tokens: thinkingBudget, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }; + } else if (thinkingType === "adaptive") { +@@ -1062,7 +1074,12 @@ var BedrockChatLanguageModel = class { + ...bedrockOptions.additionalModelRequestFields, + thinking: { + type: "adaptive", +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }; + } diff --git a/patches/@ai-sdk%2Fanthropic@3.0.111.patch b/patches/@ai-sdk%2Fanthropic@3.0.111.patch new file mode 100644 index 0000000000..82003f9c20 --- /dev/null +++ b/patches/@ai-sdk%2Fanthropic@3.0.111.patch @@ -0,0 +1,528 @@ +diff --git a/dist/index.d.mts b/dist/index.d.mts +index a66d061f466c89a57efec33f37b7daabaaba2892..15f1b2aca0c0b43e5e1f8baddd2669c074912d18 100644 +--- a/dist/index.d.mts ++++ b/dist/index.d.mts +@@ -211,9 +211,21 @@ declare const anthropicLanguageModelOptions: z.ZodObject<{ + omitted: "omitted"; + summarized: "summarized"; + }>>; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"enabled">; + budgetTokens: z.ZodOptional; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"disabled">; + }, z.core.$strip>]>>; +diff --git a/dist/index.d.ts b/dist/index.d.ts +index a66d061f466c89a57efec33f37b7daabaaba2892..15f1b2aca0c0b43e5e1f8baddd2669c074912d18 100644 +--- a/dist/index.d.ts ++++ b/dist/index.d.ts +@@ -211,9 +211,21 @@ declare const anthropicLanguageModelOptions: z.ZodObject<{ + omitted: "omitted"; + summarized: "summarized"; + }>>; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"enabled">; + budgetTokens: z.ZodOptional; ++ blockBinding: z.ZodOptional; ++ }, z.core.$strip>>; + }, z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"disabled">; + }, z.core.$strip>]>>; +diff --git a/dist/index.js b/dist/index.js +index 88c1aa865339baa320cf80ec543bed865825bc61..5006e82b0edd9676d1650cc94ecb573a2810acce 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -85,6 +85,13 @@ var anthropicMessagesResponseSchema = (0, import_provider_utils2.lazySchema)( + type: import_v42.z.literal("message"), + id: import_v42.z.string().nullish(), + model: import_v42.z.string().nullish(), ++ input_transformations: import_v42.z.array( ++ import_v42.z.looseObject({ ++ type: import_v42.z.string(), ++ path: import_v42.z.string().nullish(), ++ reason: import_v42.z.string().nullish() ++ }) ++ ).nullish(), + content: import_v42.z.array( + import_v42.z.discriminatedUnion("type", [ + import_v42.z.object({ +@@ -424,6 +431,13 @@ var anthropicMessagesChunkSchema = (0, import_provider_utils2.lazySchema)( + id: import_v42.z.string().nullish(), + model: import_v42.z.string().nullish(), + role: import_v42.z.string().nullish(), ++ input_transformations: import_v42.z.array( ++ import_v42.z.looseObject({ ++ type: import_v42.z.string(), ++ path: import_v42.z.string().nullish(), ++ reason: import_v42.z.string().nullish() ++ }) ++ ).nullish(), + usage: import_v42.z.looseObject({ + input_tokens: import_v42.z.number(), + cache_creation_input_tokens: import_v42.z.number().nullish(), +@@ -923,12 +937,18 @@ var anthropicLanguageModelOptions = import_v43.z.object({ + * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+). + * - `"summarized"`: Thinking content is returned. Required to see reasoning output. + */ +- display: import_v43.z.enum(["omitted", "summarized"]).optional() ++ display: import_v43.z.enum(["omitted", "summarized"]).optional(), ++ blockBinding: import_v43.z.object({ ++ prefixMismatchBehavior: import_v43.z.enum(["error", "drop_block"]) ++ }).optional() + }), + import_v43.z.object({ + /** for models before Opus 4.6, except Sonnet 4.6 still supports it */ + type: import_v43.z.literal("enabled"), +- budgetTokens: import_v43.z.number().optional() ++ budgetTokens: import_v43.z.number().optional(), ++ blockBinding: import_v43.z.object({ ++ prefixMismatchBehavior: import_v43.z.enum(["error", "drop_block"]) ++ }).optional() + }), + import_v43.z.object({ + type: import_v43.z.literal("disabled") +@@ -3568,6 +3588,7 @@ var AnthropicMessagesLanguageModel = class { + const sendThinking = isThinking || thinkingType === "disabled"; + let thinkingBudget = thinkingType === "enabled" ? (_g = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _g.budgetTokens : void 0; + const thinkingDisplay = thinkingType === "adaptive" ? (_h = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _h.display : void 0; ++ const thinkingBlockBinding = isThinking ? (anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : anthropicOptions.thinking.blockBinding : void 0; + const maxTokens = maxOutputTokens != null ? maxOutputTokens : maxOutputTokensForModel; + const baseArgs = { + // model id: +@@ -3583,7 +3604,12 @@ var AnthropicMessagesLanguageModel = class { + thinking: { + type: thinkingType, + ...thinkingBudget != null && { budget_tokens: thinkingBudget }, +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }, + ...((anthropicOptions == null ? void 0 : anthropicOptions.effort) || (anthropicOptions == null ? void 0 : anthropicOptions.taskBudget) || useStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) && { +@@ -3798,6 +3824,9 @@ var AnthropicMessagesLanguageModel = class { + } else if ((anthropicOptions == null ? void 0 : anthropicOptions.fallbacks) && anthropicOptions.fallbacks.length > 0) { + betas.add("server-side-fallback-2026-06-01"); + } ++ if (thinkingBlockBinding != null) { ++ betas.add("thinking-binding-controls-2026-08-01"); ++ } + const defaultEagerInputStreaming = stream && ((_j = anthropicOptions == null ? void 0 : anthropicOptions.toolStreaming) != null ? _j : true); + const { + tools: anthropicTools2, +@@ -4364,6 +4393,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens: (_a2 = response.usage.cache_creation_input_tokens) != null ? _a2 : null, + stopSequence: (_b2 = response.stop_sequence) != null ? _b2 : null, + ...stopDetails != null ? { stopDetails } : {}, ++ ...response.input_transformations != null ? { inputTransformations: response.input_transformations } : {}, + iterations: response.usage.iterations ? response.usage.iterations.map( + (iter) => ({ + type: iter.type, +@@ -4453,6 +4483,7 @@ var AnthropicMessagesLanguageModel = class { + let cacheCreationInputTokens = null; + let stopSequence = null; + let stopDetails = void 0; ++ let inputTransformations = void 0; + let container = null; + let isJsonResponseFromTool = false; + let isMessageOpen = false; +@@ -5111,6 +5142,9 @@ var AnthropicMessagesLanguageModel = class { + isMessageOpen = true; + activeMessageId = value.message.id; + usage.input_tokens = value.message.usage.input_tokens; ++ if (value.message.input_transformations != null) { ++ inputTransformations = value.message.input_transformations; ++ } + usage.cache_read_input_tokens = (_e = value.message.usage.cache_read_input_tokens) != null ? _e : 0; + usage.cache_creation_input_tokens = (_f = value.message.usage.cache_creation_input_tokens) != null ? _f : 0; + rawUsage = { +@@ -5231,6 +5265,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens, + stopSequence, + ...stopDetails != null ? { stopDetails } : {}, ++ ...inputTransformations != null ? { inputTransformations } : {}, + iterations: usage.iterations ? usage.iterations.map( + (iter) => ({ + type: iter.type, +diff --git a/dist/index.mjs b/dist/index.mjs +index 593b030c7f2adbe0cd38556b7a74f5ede6a5435e..1fa36ccb119715df3cd00976e479c679c66d2261 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -85,6 +85,13 @@ var anthropicMessagesResponseSchema = lazySchema2( + type: z2.literal("message"), + id: z2.string().nullish(), + model: z2.string().nullish(), ++ input_transformations: z2.array( ++ z2.looseObject({ ++ type: z2.string(), ++ path: z2.string().nullish(), ++ reason: z2.string().nullish() ++ }) ++ ).nullish(), + content: z2.array( + z2.discriminatedUnion("type", [ + z2.object({ +@@ -424,6 +431,13 @@ var anthropicMessagesChunkSchema = lazySchema2( + id: z2.string().nullish(), + model: z2.string().nullish(), + role: z2.string().nullish(), ++ input_transformations: z2.array( ++ z2.looseObject({ ++ type: z2.string(), ++ path: z2.string().nullish(), ++ reason: z2.string().nullish() ++ }) ++ ).nullish(), + usage: z2.looseObject({ + input_tokens: z2.number(), + cache_creation_input_tokens: z2.number().nullish(), +@@ -923,12 +937,18 @@ var anthropicLanguageModelOptions = z3.object({ + * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+). + * - `"summarized"`: Thinking content is returned. Required to see reasoning output. + */ +- display: z3.enum(["omitted", "summarized"]).optional() ++ display: z3.enum(["omitted", "summarized"]).optional(), ++ blockBinding: z3.object({ ++ prefixMismatchBehavior: z3.enum(["error", "drop_block"]) ++ }).optional() + }), + z3.object({ + /** for models before Opus 4.6, except Sonnet 4.6 still supports it */ + type: z3.literal("enabled"), +- budgetTokens: z3.number().optional() ++ budgetTokens: z3.number().optional(), ++ blockBinding: z3.object({ ++ prefixMismatchBehavior: z3.enum(["error", "drop_block"]) ++ }).optional() + }), + z3.object({ + type: z3.literal("disabled") +@@ -3620,6 +3640,7 @@ var AnthropicMessagesLanguageModel = class { + const sendThinking = isThinking || thinkingType === "disabled"; + let thinkingBudget = thinkingType === "enabled" ? (_g = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _g.budgetTokens : void 0; + const thinkingDisplay = thinkingType === "adaptive" ? (_h = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _h.display : void 0; ++ const thinkingBlockBinding = isThinking ? (anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : anthropicOptions.thinking.blockBinding : void 0; + const maxTokens = maxOutputTokens != null ? maxOutputTokens : maxOutputTokensForModel; + const baseArgs = { + // model id: +@@ -3635,7 +3656,12 @@ var AnthropicMessagesLanguageModel = class { + thinking: { + type: thinkingType, + ...thinkingBudget != null && { budget_tokens: thinkingBudget }, +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }, + ...((anthropicOptions == null ? void 0 : anthropicOptions.effort) || (anthropicOptions == null ? void 0 : anthropicOptions.taskBudget) || useStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) && { +@@ -3850,6 +3876,9 @@ var AnthropicMessagesLanguageModel = class { + } else if ((anthropicOptions == null ? void 0 : anthropicOptions.fallbacks) && anthropicOptions.fallbacks.length > 0) { + betas.add("server-side-fallback-2026-06-01"); + } ++ if (thinkingBlockBinding != null) { ++ betas.add("thinking-binding-controls-2026-08-01"); ++ } + const defaultEagerInputStreaming = stream && ((_j = anthropicOptions == null ? void 0 : anthropicOptions.toolStreaming) != null ? _j : true); + const { + tools: anthropicTools2, +@@ -4416,6 +4445,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens: (_a2 = response.usage.cache_creation_input_tokens) != null ? _a2 : null, + stopSequence: (_b2 = response.stop_sequence) != null ? _b2 : null, + ...stopDetails != null ? { stopDetails } : {}, ++ ...response.input_transformations != null ? { inputTransformations: response.input_transformations } : {}, + iterations: response.usage.iterations ? response.usage.iterations.map( + (iter) => ({ + type: iter.type, +@@ -4505,6 +4535,7 @@ var AnthropicMessagesLanguageModel = class { + let cacheCreationInputTokens = null; + let stopSequence = null; + let stopDetails = void 0; ++ let inputTransformations = void 0; + let container = null; + let isJsonResponseFromTool = false; + let isMessageOpen = false; +@@ -5163,6 +5194,9 @@ var AnthropicMessagesLanguageModel = class { + isMessageOpen = true; + activeMessageId = value.message.id; + usage.input_tokens = value.message.usage.input_tokens; ++ if (value.message.input_transformations != null) { ++ inputTransformations = value.message.input_transformations; ++ } + usage.cache_read_input_tokens = (_e = value.message.usage.cache_read_input_tokens) != null ? _e : 0; + usage.cache_creation_input_tokens = (_f = value.message.usage.cache_creation_input_tokens) != null ? _f : 0; + rawUsage = { +@@ -5283,6 +5317,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens, + stopSequence, + ...stopDetails != null ? { stopDetails } : {}, ++ ...inputTransformations != null ? { inputTransformations } : {}, + iterations: usage.iterations ? usage.iterations.map( + (iter) => ({ + type: iter.type, +diff --git a/dist/internal/index.js b/dist/internal/index.js +index 701ca5a333d83d384f682667b9976b7f72b0de37..ce53d0c9fe5c00a092b0347b45d61d8ce9c3a79a 100644 +--- a/dist/internal/index.js ++++ b/dist/internal/index.js +@@ -79,6 +79,13 @@ var anthropicMessagesResponseSchema = (0, import_provider_utils2.lazySchema)( + type: import_v42.z.literal("message"), + id: import_v42.z.string().nullish(), + model: import_v42.z.string().nullish(), ++ input_transformations: import_v42.z.array( ++ import_v42.z.looseObject({ ++ type: import_v42.z.string(), ++ path: import_v42.z.string().nullish(), ++ reason: import_v42.z.string().nullish() ++ }) ++ ).nullish(), + content: import_v42.z.array( + import_v42.z.discriminatedUnion("type", [ + import_v42.z.object({ +@@ -418,6 +425,13 @@ var anthropicMessagesChunkSchema = (0, import_provider_utils2.lazySchema)( + id: import_v42.z.string().nullish(), + model: import_v42.z.string().nullish(), + role: import_v42.z.string().nullish(), ++ input_transformations: import_v42.z.array( ++ import_v42.z.looseObject({ ++ type: import_v42.z.string(), ++ path: import_v42.z.string().nullish(), ++ reason: import_v42.z.string().nullish() ++ }) ++ ).nullish(), + usage: import_v42.z.looseObject({ + input_tokens: import_v42.z.number(), + cache_creation_input_tokens: import_v42.z.number().nullish(), +@@ -917,12 +931,18 @@ var anthropicLanguageModelOptions = import_v43.z.object({ + * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+). + * - `"summarized"`: Thinking content is returned. Required to see reasoning output. + */ +- display: import_v43.z.enum(["omitted", "summarized"]).optional() ++ display: import_v43.z.enum(["omitted", "summarized"]).optional(), ++ blockBinding: import_v43.z.object({ ++ prefixMismatchBehavior: import_v43.z.enum(["error", "drop_block"]) ++ }).optional() + }), + import_v43.z.object({ + /** for models before Opus 4.6, except Sonnet 4.6 still supports it */ + type: import_v43.z.literal("enabled"), +- budgetTokens: import_v43.z.number().optional() ++ budgetTokens: import_v43.z.number().optional(), ++ blockBinding: import_v43.z.object({ ++ prefixMismatchBehavior: import_v43.z.enum(["error", "drop_block"]) ++ }).optional() + }), + import_v43.z.object({ + type: import_v43.z.literal("disabled") +@@ -3562,6 +3582,7 @@ var AnthropicMessagesLanguageModel = class { + const sendThinking = isThinking || thinkingType === "disabled"; + let thinkingBudget = thinkingType === "enabled" ? (_g = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _g.budgetTokens : void 0; + const thinkingDisplay = thinkingType === "adaptive" ? (_h = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _h.display : void 0; ++ const thinkingBlockBinding = isThinking ? (anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : anthropicOptions.thinking.blockBinding : void 0; + const maxTokens = maxOutputTokens != null ? maxOutputTokens : maxOutputTokensForModel; + const baseArgs = { + // model id: +@@ -3577,7 +3598,12 @@ var AnthropicMessagesLanguageModel = class { + thinking: { + type: thinkingType, + ...thinkingBudget != null && { budget_tokens: thinkingBudget }, +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }, + ...((anthropicOptions == null ? void 0 : anthropicOptions.effort) || (anthropicOptions == null ? void 0 : anthropicOptions.taskBudget) || useStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) && { +@@ -3792,6 +3818,9 @@ var AnthropicMessagesLanguageModel = class { + } else if ((anthropicOptions == null ? void 0 : anthropicOptions.fallbacks) && anthropicOptions.fallbacks.length > 0) { + betas.add("server-side-fallback-2026-06-01"); + } ++ if (thinkingBlockBinding != null) { ++ betas.add("thinking-binding-controls-2026-08-01"); ++ } + const defaultEagerInputStreaming = stream && ((_j = anthropicOptions == null ? void 0 : anthropicOptions.toolStreaming) != null ? _j : true); + const { + tools: anthropicTools2, +@@ -4358,6 +4387,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens: (_a2 = response.usage.cache_creation_input_tokens) != null ? _a2 : null, + stopSequence: (_b2 = response.stop_sequence) != null ? _b2 : null, + ...stopDetails != null ? { stopDetails } : {}, ++ ...response.input_transformations != null ? { inputTransformations: response.input_transformations } : {}, + iterations: response.usage.iterations ? response.usage.iterations.map( + (iter) => ({ + type: iter.type, +@@ -4447,6 +4477,7 @@ var AnthropicMessagesLanguageModel = class { + let cacheCreationInputTokens = null; + let stopSequence = null; + let stopDetails = void 0; ++ let inputTransformations = void 0; + let container = null; + let isJsonResponseFromTool = false; + let isMessageOpen = false; +@@ -5105,6 +5136,9 @@ var AnthropicMessagesLanguageModel = class { + isMessageOpen = true; + activeMessageId = value.message.id; + usage.input_tokens = value.message.usage.input_tokens; ++ if (value.message.input_transformations != null) { ++ inputTransformations = value.message.input_transformations; ++ } + usage.cache_read_input_tokens = (_e = value.message.usage.cache_read_input_tokens) != null ? _e : 0; + usage.cache_creation_input_tokens = (_f = value.message.usage.cache_creation_input_tokens) != null ? _f : 0; + rawUsage = { +@@ -5225,6 +5259,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens, + stopSequence, + ...stopDetails != null ? { stopDetails } : {}, ++ ...inputTransformations != null ? { inputTransformations } : {}, + iterations: usage.iterations ? usage.iterations.map( + (iter) => ({ + type: iter.type, +diff --git a/dist/internal/index.mjs b/dist/internal/index.mjs +index c11f29fa42a9ce6a5681d43c609020f1f4a616da..df7f7a01992a56826eb809e6cdfb1e50a87a1b90 100644 +--- a/dist/internal/index.mjs ++++ b/dist/internal/index.mjs +@@ -69,6 +69,13 @@ var anthropicMessagesResponseSchema = lazySchema2( + type: z2.literal("message"), + id: z2.string().nullish(), + model: z2.string().nullish(), ++ input_transformations: z2.array( ++ z2.looseObject({ ++ type: z2.string(), ++ path: z2.string().nullish(), ++ reason: z2.string().nullish() ++ }) ++ ).nullish(), + content: z2.array( + z2.discriminatedUnion("type", [ + z2.object({ +@@ -408,6 +415,13 @@ var anthropicMessagesChunkSchema = lazySchema2( + id: z2.string().nullish(), + model: z2.string().nullish(), + role: z2.string().nullish(), ++ input_transformations: z2.array( ++ z2.looseObject({ ++ type: z2.string(), ++ path: z2.string().nullish(), ++ reason: z2.string().nullish() ++ }) ++ ).nullish(), + usage: z2.looseObject({ + input_tokens: z2.number(), + cache_creation_input_tokens: z2.number().nullish(), +@@ -907,12 +921,18 @@ var anthropicLanguageModelOptions = z3.object({ + * - `"omitted"`: Thinking blocks are present but text is empty (default for Opus 4.7+). + * - `"summarized"`: Thinking content is returned. Required to see reasoning output. + */ +- display: z3.enum(["omitted", "summarized"]).optional() ++ display: z3.enum(["omitted", "summarized"]).optional(), ++ blockBinding: z3.object({ ++ prefixMismatchBehavior: z3.enum(["error", "drop_block"]) ++ }).optional() + }), + z3.object({ + /** for models before Opus 4.6, except Sonnet 4.6 still supports it */ + type: z3.literal("enabled"), +- budgetTokens: z3.number().optional() ++ budgetTokens: z3.number().optional(), ++ blockBinding: z3.object({ ++ prefixMismatchBehavior: z3.enum(["error", "drop_block"]) ++ }).optional() + }), + z3.object({ + type: z3.literal("disabled") +@@ -3604,6 +3624,7 @@ var AnthropicMessagesLanguageModel = class { + const sendThinking = isThinking || thinkingType === "disabled"; + let thinkingBudget = thinkingType === "enabled" ? (_g = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _g.budgetTokens : void 0; + const thinkingDisplay = thinkingType === "adaptive" ? (_h = anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : _h.display : void 0; ++ const thinkingBlockBinding = isThinking ? (anthropicOptions == null ? void 0 : anthropicOptions.thinking) == null ? void 0 : anthropicOptions.thinking.blockBinding : void 0; + const maxTokens = maxOutputTokens != null ? maxOutputTokens : maxOutputTokensForModel; + const baseArgs = { + // model id: +@@ -3619,7 +3640,12 @@ var AnthropicMessagesLanguageModel = class { + thinking: { + type: thinkingType, + ...thinkingBudget != null && { budget_tokens: thinkingBudget }, +- ...thinkingDisplay != null && { display: thinkingDisplay } ++ ...thinkingDisplay != null && { display: thinkingDisplay }, ++ ...thinkingBlockBinding != null && { ++ block_binding: { ++ prefix_mismatch_behavior: thinkingBlockBinding.prefixMismatchBehavior ++ } ++ } + } + }, + ...((anthropicOptions == null ? void 0 : anthropicOptions.effort) || (anthropicOptions == null ? void 0 : anthropicOptions.taskBudget) || useStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) && { +@@ -3834,6 +3860,9 @@ var AnthropicMessagesLanguageModel = class { + } else if ((anthropicOptions == null ? void 0 : anthropicOptions.fallbacks) && anthropicOptions.fallbacks.length > 0) { + betas.add("server-side-fallback-2026-06-01"); + } ++ if (thinkingBlockBinding != null) { ++ betas.add("thinking-binding-controls-2026-08-01"); ++ } + const defaultEagerInputStreaming = stream && ((_j = anthropicOptions == null ? void 0 : anthropicOptions.toolStreaming) != null ? _j : true); + const { + tools: anthropicTools2, +@@ -4400,6 +4429,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens: (_a2 = response.usage.cache_creation_input_tokens) != null ? _a2 : null, + stopSequence: (_b2 = response.stop_sequence) != null ? _b2 : null, + ...stopDetails != null ? { stopDetails } : {}, ++ ...response.input_transformations != null ? { inputTransformations: response.input_transformations } : {}, + iterations: response.usage.iterations ? response.usage.iterations.map( + (iter) => ({ + type: iter.type, +@@ -4489,6 +4519,7 @@ var AnthropicMessagesLanguageModel = class { + let cacheCreationInputTokens = null; + let stopSequence = null; + let stopDetails = void 0; ++ let inputTransformations = void 0; + let container = null; + let isJsonResponseFromTool = false; + let isMessageOpen = false; +@@ -5147,6 +5178,9 @@ var AnthropicMessagesLanguageModel = class { + isMessageOpen = true; + activeMessageId = value.message.id; + usage.input_tokens = value.message.usage.input_tokens; ++ if (value.message.input_transformations != null) { ++ inputTransformations = value.message.input_transformations; ++ } + usage.cache_read_input_tokens = (_e = value.message.usage.cache_read_input_tokens) != null ? _e : 0; + usage.cache_creation_input_tokens = (_f = value.message.usage.cache_creation_input_tokens) != null ? _f : 0; + rawUsage = { +@@ -5267,6 +5301,7 @@ var AnthropicMessagesLanguageModel = class { + cacheCreationInputTokens, + stopSequence, + ...stopDetails != null ? { stopDetails } : {}, ++ ...inputTransformations != null ? { inputTransformations } : {}, + iterations: usage.iterations ? usage.iterations.map( + (iter) => ({ + type: iter.type,