From 1cc9ca99ac8d030e1d9c6023fb17ee4c8ccbc180 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 18:38:17 -0600 Subject: [PATCH 01/17] wip: add transform --- packages/opencode/src/provider/transform.ts | 118 +++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 407f7351b5b6..2fead40f9310 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -243,6 +243,116 @@ export namespace ProviderTransform { return undefined } + export function thinking(model: Provider.Model, thinking: MessageV2.Thinking): Record { + if (!model.capabilities.reasoning || thinking.effort === "default") return {} + + switch (model.api.npm) { + case "@openrouter/ai-sdk-provider": + return { + reasoning: { effort: thinking.effort }, + } + + // TODO: YOU CANNOT SET max_tokens if this is set!!! + case "@ai-sdk/gateway": + return { + reasoningEffort: thinking.effort, + } + + case "@ai-sdk/cerebras": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cerebras + case "@ai-sdk/togetherai": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/togetherai + case "@ai-sdk/xai": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/xai + case "@ai-sdk/deepinfra": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/deepinfra + case "@ai-sdk/openai-compatible": + const result: Record = { + reasoningEffort: thinking.effort, + } + + if (model.providerID === "baseten") { + result["chat_template_args"] = { enable_thinking: true } + } + + return result + + case "@ai-sdk/azure": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure + case "@ai-sdk/openai": + return { + reasoningEffort: thinking.effort, + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + } + + case "@ai-sdk/anthropic": + return { + thinking: { + type: "enabled", + budgetTokens: thinking.effort === "medium" ? 16000 : 31999, + }, + } + + case "@ai-sdk/amazon-bedrock": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock + return { + reasoningConfig: { + type: "enabled", + maxReasoningEffort: thinking.effort, + }, + } + + case "@ai-sdk/google-vertex": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex + case "@ai-sdk/google": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai + if (model.id.includes("2.5")) { + return { + thinkingConfig: { + includeThoughts: true, + thinkingBudget: thinking.effort === "medium" ? 8192 : 24576, + }, + } + } + return { + thinkingConfig: { + includeThoughts: true, + thinkingLevel: thinking.effort, + }, + } + + case "@ai-sdk/mistral": + // TODO: implement mistral thinking options + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/mistral + return {} + + case "@ai-sdk/cohere": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cohere + const res: Record = { + thinking: { + type: "enabled", + }, + } + if (thinking.effort === "medium") { + res["thinking"]["budgetTokens"] = 8192 + } + return res + + case "@ai-sdk/groq": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/groq + return { + reasoningFormat: "parsed", + reasoningEffort: thinking.effort, + } + + case "@ai-sdk/perplexity": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/perplexity + return {} + } + return {} + } + export function options( model: Provider.Model, sessionID: string, @@ -255,7 +365,7 @@ export namespace ProviderTransform { include: true, } if (model.api.id.includes("gemini-3")) { - result["reasoning"] = { effort: "high" } + // result["reasoning"] = { effort: "high" } } } @@ -275,7 +385,7 @@ export namespace ProviderTransform { includeThoughts: true, } if (model.api.id.includes("gemini-3")) { - result["thinkingConfig"]["thinkingLevel"] = "high" + // result["thinkingConfig"]["thinkingLevel"] = "high" } } @@ -285,7 +395,7 @@ export namespace ProviderTransform { } if (!model.api.id.includes("codex") && !model.api.id.includes("gpt-5-pro")) { - result["reasoningEffort"] = "medium" + // result["reasoningEffort"] = "medium" } if (model.api.id.endsWith("gpt-5.") && model.providerID !== "azure") { @@ -322,6 +432,7 @@ export namespace ProviderTransform { export function providerOptions(model: Provider.Model, options: { [x: string]: any }) { switch (model.api.npm) { + case "@ai-sdk/github-copilot": case "@ai-sdk/openai": case "@ai-sdk/azure": return { @@ -335,6 +446,7 @@ export namespace ProviderTransform { return { ["anthropic" as string]: options, } + case "@ai-sdk/google-vertex": case "@ai-sdk/google": return { ["google" as string]: options, From 16fb216b116aab6d91cbd791d9da852486f3dafb Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 18:40:41 -0600 Subject: [PATCH 02/17] wip: update deps & add thinking field --- bun.lock | 31 ++++++++++----------- packages/opencode/package.json | 21 +++++++------- packages/opencode/src/session/message-v2.ts | 4 +++ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bun.lock b/bun.lock index fa5d84e4ea36..0fb03f6be0fa 100644 --- a/bun.lock +++ b/bun.lock @@ -254,19 +254,18 @@ "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.5.1", "@ai-sdk/amazon-bedrock": "3.0.57", - "@ai-sdk/anthropic": "2.0.50", - "@ai-sdk/azure": "2.0.73", + "@ai-sdk/anthropic": "2.0.54", + "@ai-sdk/azure": "2.0.82", "@ai-sdk/cerebras": "1.0.33", "@ai-sdk/cohere": "2.0.21", "@ai-sdk/deepinfra": "1.0.30", "@ai-sdk/gateway": "2.0.23", - "@ai-sdk/google": "2.0.44", + "@ai-sdk/google": "2.0.49", "@ai-sdk/google-vertex": "3.0.81", "@ai-sdk/groq": "2.0.33", - "@ai-sdk/mcp": "0.0.8", "@ai-sdk/mistral": "2.0.26", "@ai-sdk/openai": "2.0.71", - "@ai-sdk/openai-compatible": "1.0.27", + "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/perplexity": "2.0.22", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", @@ -538,7 +537,7 @@ "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="], - "@ai-sdk/azure": ["@ai-sdk/azure@2.0.73", "", { "dependencies": { "@ai-sdk/openai": "2.0.71", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LpAg3Ak/V3WOemBu35Qbx9jfQfApsHNXX9p3bXVsnRu3XXi1QQUt5gMOCIb4znPonz+XnHenIDZMBwdsb1TfRQ=="], + "@ai-sdk/azure": ["@ai-sdk/azure@2.0.82", "", { "dependencies": { "@ai-sdk/openai": "2.0.80", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Bpab51ETBB4adZC1xGMYsryL/CB8j1sA+t5aDqhRv3t3WRLTxhaBDcFKtQTIuxiEQTFosz9Q2xQqdfBvQm5jHw=="], "@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w=="], @@ -548,14 +547,12 @@ "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qmX7afPRszUqG5hryHF3UN8ITPIRSGmDW6VYCmByzjoUkgm3MekzSx2hMV1wr0P+llDeuXb378SjqUfpvWJulg=="], - "@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="], + "@ai-sdk/google": ["@ai-sdk/google@2.0.49", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-efwKk4mOV0SpumUaQskeYABk37FJPmEYwoDJQEjyLRmGSjtHRe9P5Cwof5ffLvaFav2IaJpBGEz98pyTs7oNWA=="], "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="], "@ai-sdk/groq": ["@ai-sdk/groq@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig=="], - "@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="], - "@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.26", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA=="], "@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="], @@ -3892,9 +3889,7 @@ "@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="], - "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="], - - "@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.80", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tNHuraF11db+8xJEDBoU9E3vMcpnHFKRhnLQ3DQX2LnEzfPB9DksZ8rE+yVuDN1WRW9cm2OWAhgHFgVKs7ICuw=="], "@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], @@ -3908,11 +3903,13 @@ "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="], - "@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="], - "@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], "@ai-sdk/mistral/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], @@ -4308,11 +4305,11 @@ "nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], - "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="], + "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.54", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6OSFMkt5NkAchH7o0W+dI2h6yR8EPXx7Yl6txyh0gadLlkf1UU/ScyoYlkxAW8UtGju/+apvwVTdLYEQuIsVVQ=="], "opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="], - "opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bpYruxVLhrTbVH6CCq48zMJNeHu6FmHtEedl9FXckEgcIEAi036idFhJlcRwC1jNCwlacbzb8dPD7OAH1EKJaQ=="], + "opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="], @@ -4916,7 +4913,7 @@ "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], "opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 68dfb3c0a117..e0459aa41c0f 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -50,24 +50,23 @@ "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.5.1", "@ai-sdk/amazon-bedrock": "3.0.57", - "@ai-sdk/anthropic": "2.0.50", - "@ai-sdk/azure": "2.0.73", + "@ai-sdk/anthropic": "2.0.54", + "@ai-sdk/azure": "2.0.82", + "@ai-sdk/google": "2.0.49", + "@ai-sdk/google-vertex": "3.0.81", + "@ai-sdk/mistral": "2.0.26", + "@ai-sdk/openai": "2.0.71", + "@ai-sdk/openai-compatible": "1.0.29", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.18", + "@ai-sdk/xai": "2.0.42", "@ai-sdk/cerebras": "1.0.33", "@ai-sdk/cohere": "2.0.21", "@ai-sdk/deepinfra": "1.0.30", "@ai-sdk/gateway": "2.0.23", - "@ai-sdk/google": "2.0.44", - "@ai-sdk/google-vertex": "3.0.81", "@ai-sdk/groq": "2.0.33", - "@ai-sdk/mcp": "0.0.8", - "@ai-sdk/mistral": "2.0.26", - "@ai-sdk/openai": "2.0.71", - "@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/perplexity": "2.0.22", - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.18", "@ai-sdk/togetherai": "1.0.30", - "@ai-sdk/xai": "2.0.42", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 95bc3812e7a4..4349e75044dc 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -289,6 +289,9 @@ export namespace MessageV2 { sessionID: z.string(), }) + export const Thinking = z.object({ effort: z.enum(["default", "medium", "high"]) }) + export type Thinking = z.infer + export const User = Base.extend({ role: z.literal("user"), time: z.object({ @@ -308,6 +311,7 @@ export namespace MessageV2 { }), system: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), + thinking: Thinking.optional(), }).meta({ ref: "UserMessage", }) From e484e6ff1da5d5fdb8c4cd60747656910e7ca17d Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 18:43:10 -0600 Subject: [PATCH 03/17] wip: small fixes & add thinking --- packages/opencode/src/provider/transform.ts | 3 ++- packages/opencode/src/session/llm.ts | 15 ++++++++------- packages/opencode/src/session/message-v2.ts | 2 -- packages/opencode/src/session/prompt.ts | 2 ++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 2fead40f9310..a6da2165fd85 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -3,6 +3,7 @@ import { unique } from "remeda" import type { JSONSchema } from "zod/v4/core" import type { Provider } from "./provider" import type { ModelsDev } from "./models" +import type { MessageV2 } from "@/session/message-v2" type Modality = NonNullable["input"][number] @@ -124,7 +125,7 @@ export namespace ProviderTransform { cacheControl: { type: "ephemeral" }, }, openrouter: { - cache_control: { type: "ephemeral" }, + cacheControl: { type: "ephemeral" }, }, bedrock: { cachePoint: { type: "ephemeral" }, diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index a81aa7db2241..36be190458e2 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -74,6 +74,13 @@ export namespace LLM { } const provider = await Provider.getProvider(input.model.providerID) + const options = pipe( + ProviderTransform.options(input.model, input.sessionID, provider.options), + mergeDeep(input.small ? ProviderTransform.smallOptions(input.model) : {}), + mergeDeep(input.model.options), + mergeDeep(input.agent.options), + mergeDeep(input.user.thinking ? ProviderTransform.thinking(input.model, input.user.thinking) : {}), + ) const params = await Plugin.trigger( "chat.params", @@ -90,13 +97,7 @@ export namespace LLM { : undefined, topP: input.agent.topP ?? ProviderTransform.topP(input.model), topK: ProviderTransform.topK(input.model), - options: pipe( - {}, - mergeDeep(ProviderTransform.options(input.model, input.sessionID, provider.options)), - input.small ? mergeDeep(ProviderTransform.smallOptions(input.model)) : mergeDeep({}), - mergeDeep(input.model.options), - mergeDeep(input.agent.options), - ), + options, }, ) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 4349e75044dc..81cd3a4f550a 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -1,8 +1,6 @@ import { BusEvent } from "@/bus/bus-event" -import { Bus } from "@/bus" import z from "zod" import { NamedError } from "@opencode-ai/util/error" -import { Message } from "./message" import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai" import { Identifier } from "../id/id" import { LSP } from "../lsp" diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 19dc90b3bcbd..3b3c28d017d3 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -90,6 +90,7 @@ export namespace SessionPrompt { noReply: z.boolean().optional(), tools: z.record(z.string(), z.boolean()).optional(), system: z.string().optional(), + thinking: MessageV2.Thinking.optional(), parts: z.array( z.discriminatedUnion("type", [ MessageV2.TextPart.omit({ @@ -727,6 +728,7 @@ export namespace SessionPrompt { agent: agent.name, model: input.model ?? agent.model ?? (await lastModel(input.sessionID)), system: input.system, + thinking: input.thinking, } const parts = await Promise.all( From 16a17aed0f28f50c7655b29991aa042d9bb06c82 Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 29 Dec 2025 00:44:42 +0000 Subject: [PATCH 04/17] Update Nix flake.lock and hashes --- flake.lock | 6 +++--- nix/hashes.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index dcc8c594a706..6cb2cf919712 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1766840161, - "narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=", + "lastModified": 1766870016, + "narHash": "sha256-fHmxAesa6XNqnIkcS6+nIHuEmgd/iZSP/VXxweiEuQw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1", + "rev": "5c2bc52fb9f8c264ed6c93bd20afa2ff5e763dce", "type": "github" }, "original": { diff --git a/nix/hashes.json b/nix/hashes.json index f43b14684c33..60707938305d 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-lloUZt5mLyNWAcbQrJB4wGUKvKu24WFEhOLfZD5/FMg=" + "nodeModules": "sha256-bpTD7ANhuYhO/J0k/tWhXrmN0Og4M0g8IbNLY7ls968=" } From 19bba7bda61913a5ce3d7555c3b4a9da419e0e21 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 18:51:17 -0600 Subject: [PATCH 05/17] add toggle & regen sdk --- .../cli/cmd/tui/component/prompt/index.tsx | 38 +++++++++++++++++++ .../src/cli/cmd/tui/context/local.tsx | 24 ++++++++++++ packages/opencode/src/config/config.ts | 1 + packages/sdk/js/src/v2/gen/sdk.gen.ts | 8 ++++ packages/sdk/js/src/v2/gen/types.gen.ts | 13 +++++++ 5 files changed, 84 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index f819746d53c0..cb06f491da34 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -167,6 +167,13 @@ export function Prompt(props: PromptProps) { if (!props.disabled) input.cursorColor = theme.text }) + const lastUserMessage = createMemo(() => { + if (!props.sessionID) return undefined + const messages = sync.data.message[props.sessionID] + if (!messages) return undefined + return messages.findLast((m) => m.role === "user") + }) + const [store, setStore] = createStore<{ prompt: PromptInfo mode: "normal" | "shell" @@ -184,6 +191,26 @@ export function Prompt(props: PromptProps) { interrupt: 0, }) + createEffect(() => { + const msg = lastUserMessage() + if (!msg) return + + // Set agent from last message + if (msg.agent) { + local.agent.set(msg.agent) + } + + // Set model from last message + if (msg.model) { + local.model.set(msg.model) + } + + // Set effort from last message + if (msg.thinking?.effort) { + local.effort.set(msg.thinking.effort) + } + }) + command.register(() => { return [ { @@ -843,6 +870,11 @@ export function Prompt(props: PromptProps) { return } } + if (keybind.match("effort_cycle", e)) { + e.preventDefault() + local.effort.cycle() + return + } if (store.mode === "normal") autocomplete.onKeyDown(e) if (!autocomplete.visible) { if ( @@ -958,6 +990,12 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} {local.model.parsed().provider} + + · + + {local.effort.current()} + + diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index 55c04621ef36..851fe3793f82 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -51,6 +51,29 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } }) + const effort = iife(() => { + const [effortStore, setEffortStore] = createStore<{ + current: "default" | "medium" | "high" + }>({ + current: "default", + }) + + return { + current() { + return effortStore.current + }, + set(value: "default" | "medium" | "high") { + setEffortStore("current", value) + }, + cycle() { + const levels: Array<"default" | "medium" | "high"> = ["default", "medium", "high"] + const currentIndex = levels.indexOf(effortStore.current) + const nextIndex = (currentIndex + 1) % levels.length + setEffortStore("current", levels[nextIndex]) + }, + } + }) + const agent = iife(() => { const agents = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden)) const [agentStore, setAgentStore] = createStore<{ @@ -333,6 +356,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ model, agent, mcp, + effort, } return result }, diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 66f42e5a8512..6e288b26856a 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -486,6 +486,7 @@ export namespace Config { agent_list: z.string().optional().default("a").describe("List agents"), agent_cycle: z.string().optional().default("tab").describe("Next agent"), agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"), + effort_cycle: z.string().optional().default("ctrl+t").describe("Cycle thinking effort level"), input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"), input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"), input_submit: z.string().optional().default("return").describe("Submit input"), diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 797896ace9af..afb178a247bd 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -1228,6 +1228,9 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts?: Array }, options?: Options, @@ -1245,6 +1248,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, + { in: "body", key: "thinking" }, { in: "body", key: "parts" }, ], }, @@ -1314,6 +1318,9 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts?: Array }, options?: Options, @@ -1331,6 +1338,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, + { in: "body", key: "thinking" }, { in: "body", key: "parts" }, ], }, diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 5c4cc69423db..ab127f999b6b 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -90,6 +90,9 @@ export type UserMessage = { tools?: { [key: string]: boolean } + thinking?: { + effort: "default" | "medium" | "high" + } } export type ProviderAuthError = { @@ -969,6 +972,10 @@ export type KeybindsConfig = { * Previous agent */ agent_cycle_reverse?: string + /** + * Cycle thinking effort level + */ + effort_cycle?: string /** * Clear input field */ @@ -2944,6 +2951,9 @@ export type SessionPromptData = { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts: Array } path: { @@ -3127,6 +3137,9 @@ export type SessionPromptAsyncData = { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts: Array } path: { From c0987098ab98d3ee83c596ca459c20128b2f1f8a Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 18:57:15 -0600 Subject: [PATCH 06/17] ensure prompts & commands respect thinking setting --- packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 2 ++ packages/opencode/src/session/prompt.ts | 2 ++ packages/sdk/js/src/v2/gen/sdk.gen.ts | 4 ++++ packages/sdk/js/src/v2/gen/types.gen.ts | 3 +++ 4 files changed, 11 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index cb06f491da34..e6824e35e93f 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -617,6 +617,7 @@ export function Prompt(props: PromptProps) { agent: local.agent.current().name, model: `${selectedModel.providerID}/${selectedModel.modelID}`, messageID, + thinking: local.effort.current() !== "default" ? { effort: local.effort.current() } : undefined, }) } else { sdk.client.session.prompt({ @@ -625,6 +626,7 @@ export function Prompt(props: PromptProps) { messageID, agent: local.agent.current().name, model: selectedModel, + thinking: local.effort.current() !== "default" ? { effort: local.effort.current() } : undefined, parts: [ { id: Identifier.ascending("part"), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 3b3c28d017d3..93dd690b9e90 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1269,6 +1269,7 @@ export namespace SessionPrompt { model: z.string().optional(), arguments: z.string(), command: z.string(), + thinking: MessageV2.Thinking.optional(), }) export type CommandInput = z.infer const bashRegex = /!`([^`]+)`/g @@ -1371,6 +1372,7 @@ export namespace SessionPrompt { model, agent: agentName, parts, + thinking: input.thinking, })) as MessageV2.WithParts Bus.publish(Command.Event.Executed, { diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index afb178a247bd..2667d4e5542d 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -1370,6 +1370,9 @@ export class Session extends HeyApiClient { model?: string arguments?: string command?: string + thinking?: { + effort: "default" | "medium" | "high" + } }, options?: Options, ) { @@ -1385,6 +1388,7 @@ export class Session extends HeyApiClient { { in: "body", key: "model" }, { in: "body", key: "arguments" }, { in: "body", key: "command" }, + { in: "body", key: "thinking" }, ], }, ], diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index ab127f999b6b..c93b4e19f251 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -3183,6 +3183,9 @@ export type SessionCommandData = { model?: string arguments: string command: string + thinking?: { + effort: "default" | "medium" | "high" + } } path: { /** From cd23db07c3a2e0217de81d3556624dbcca4509b6 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 21:56:41 -0600 Subject: [PATCH 07/17] hide toggle for non reasoning models --- .../src/cli/cmd/tui/component/prompt/index.tsx | 11 ++++++++--- packages/opencode/src/cli/cmd/tui/context/local.tsx | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index e6824e35e93f..e578a05fc34d 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -589,6 +589,10 @@ export function Prompt(props: PromptProps) { // Capture mode before it gets reset const currentMode = store.mode + const thinking = + local.model.parsed().reasoning && local.effort.current() !== "default" + ? { effort: local.effort.current() } + : undefined if (store.mode === "shell") { sdk.client.session.shell({ @@ -617,7 +621,7 @@ export function Prompt(props: PromptProps) { agent: local.agent.current().name, model: `${selectedModel.providerID}/${selectedModel.modelID}`, messageID, - thinking: local.effort.current() !== "default" ? { effort: local.effort.current() } : undefined, + thinking, }) } else { sdk.client.session.prompt({ @@ -626,7 +630,7 @@ export function Prompt(props: PromptProps) { messageID, agent: local.agent.current().name, model: selectedModel, - thinking: local.effort.current() !== "default" ? { effort: local.effort.current() } : undefined, + thinking, parts: [ { id: Identifier.ascending("part"), @@ -874,6 +878,7 @@ export function Prompt(props: PromptProps) { } if (keybind.match("effort_cycle", e)) { e.preventDefault() + if (!local.model.parsed().reasoning) return local.effort.cycle() return } @@ -992,7 +997,7 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} {local.model.parsed().provider} - + · {local.effort.current()} diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index 851fe3793f82..d390ec41c045 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -241,6 +241,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ return { provider: "Connect a provider", model: "No provider selected", + reasoning: false, } } const provider = sync.data.provider.find((x) => x.id === value.providerID) @@ -248,6 +249,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ return { provider: provider?.name ?? value.providerID, model: info?.name ?? value.modelID, + reasoning: info?.capabilities?.reasoning ?? false, } }), cycle(direction: 1 | -1) { From 6656498f9bcfc08e8d2f4eacfafb52ac0ce8436f Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 28 Dec 2025 22:52:07 -0600 Subject: [PATCH 08/17] blacklist reasoning models that cant have variable reasoning settings --- .../cli/cmd/tui/component/prompt/index.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index e578a05fc34d..1fac566e9677 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -589,10 +589,7 @@ export function Prompt(props: PromptProps) { // Capture mode before it gets reset const currentMode = store.mode - const thinking = - local.model.parsed().reasoning && local.effort.current() !== "default" - ? { effort: local.effort.current() } - : undefined + const thinking = showEffort() ? { effort: local.effort.current() } : undefined if (store.mode === "shell") { sdk.client.session.shell({ @@ -751,6 +748,21 @@ export function Prompt(props: PromptProps) { return local.agent.color(local.agent.current().name) }) + const showEffort = createMemo(() => { + if (!local.model.parsed().reasoning) return false + if (local.effort.current() === "default") return false + const modelID = local.model.current()?.modelID?.toLowerCase() ?? "" + // until models.dev has better reasoning constructs, let's just blacklist models that cant have toggled reasoning + if ( + modelID.includes("deepseek") || + modelID.includes("minimax") || + modelID.includes("glm") || + modelID.includes("big-pickle") + ) + return false + return true + }) + const spinnerDef = createMemo(() => { const color = local.agent.color(local.agent.current().name) return { @@ -997,7 +1009,7 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} {local.model.parsed().provider} - + · {local.effort.current()} From 77c5ebdf587dd8a4342d868a96356f0d6db72ccd Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 13:23:11 -0600 Subject: [PATCH 09/17] wip --- packages/opencode/src/config/config.ts | 2 +- packages/opencode/src/provider/provider.ts | 18 ++- packages/opencode/src/provider/transform.ts | 159 +++++++++++++------- packages/opencode/src/session/message-v2.ts | 5 +- 4 files changed, 120 insertions(+), 64 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 6e288b26856a..0742518954a3 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -486,7 +486,7 @@ export namespace Config { agent_list: z.string().optional().default("a").describe("List agents"), agent_cycle: z.string().optional().default("tab").describe("Next agent"), agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"), - effort_cycle: z.string().optional().default("ctrl+t").describe("Cycle thinking effort level"), + variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"), input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"), input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"), input_submit: z.string().optional().default("return").describe("Submit input"), diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 0fdf26392f62..b465e9166040 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -34,6 +34,7 @@ import { createCohere } from "@ai-sdk/cohere" import { createGateway } from "@ai-sdk/gateway" import { createTogetherAI } from "@ai-sdk/togetherai" import { createPerplexity } from "@ai-sdk/perplexity" +import { ProviderTransform } from "./transform" export namespace Provider { const log = Log.create({ service: "provider" }) @@ -389,6 +390,16 @@ export namespace Provider { }, } + export const Variant = z + .object({ + disabled: z.boolean(), + }) + .catchall(z.any()) + .meta({ + ref: "Variant", + }) + export type Variant = z.infer + export const Model = z .object({ id: z.string(), @@ -452,6 +463,7 @@ export namespace Provider { options: z.record(z.string(), z.any()), headers: z.record(z.string(), z.string()), release_date: z.string(), + variants: z.record(z.string(), Variant).optional(), }) .meta({ ref: "Model", @@ -474,7 +486,7 @@ export namespace Provider { export type Info = z.infer function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model): Model { - return { + const m: Model = { id: model.id, providerID: provider.id, name: model.name, @@ -532,6 +544,10 @@ export namespace Provider { }, release_date: model.release_date, } + + m.variants = mapValues(ProviderTransform.variants(m), (v) => ({ disabled: false, ...v })) + + return m } export function fromModelsDevProvider(provider: ModelsDev.Provider): Info { diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index a6da2165fd85..5ffe250be8d8 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -244,20 +244,20 @@ export namespace ProviderTransform { return undefined } - export function thinking(model: Provider.Model, thinking: MessageV2.Thinking): Record { - if (!model.capabilities.reasoning || thinking.effort === "default") return {} + const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"] + const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"] + + export function variants(model: Provider.Model) { + if (!model.capabilities.reasoning) return {} switch (model.api.npm) { case "@openrouter/ai-sdk-provider": - return { - reasoning: { effort: thinking.effort }, - } + if (!model.id.includes("gpt") && !model.id.includes("gemini-3") && !model.id.includes("grok-4")) return {} + return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoning: { effort } }])) // TODO: YOU CANNOT SET max_tokens if this is set!!! case "@ai-sdk/gateway": - return { - reasoningEffort: thinking.effort, - } + return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }])) case "@ai-sdk/cerebras": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cerebras @@ -268,41 +268,76 @@ export namespace ProviderTransform { case "@ai-sdk/deepinfra": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/deepinfra case "@ai-sdk/openai-compatible": - const result: Record = { - reasoningEffort: thinking.effort, - } - - if (model.providerID === "baseten") { - result["chat_template_args"] = { enable_thinking: true } - } - - return result + return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }])) case "@ai-sdk/azure": - // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure + if (model.id === "o1-mini") return {} + const azureEfforts = ["low", "medium", "high"] + if (model.id.includes("gpt-5")) { + azureEfforts.unshift("minimal") + } + return Object.fromEntries( + azureEfforts.map((effort) => [ + effort, + { + reasoningEffort: effort, + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + ]), + ) case "@ai-sdk/openai": - return { - reasoningEffort: thinking.effort, - reasoningSummary: "auto", - include: ["reasoning.encrypted_content"], - } + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/openai + if (model.id === "gpt-5-pro") return {} + const openaiEfforts = ["minimal", ...WIDELY_SUPPORTED_EFFORTS] + if (model.release_date >= "2025-11-13") { + openaiEfforts.unshift("none") + } + if (model.release_date >= "2025-12-04") { + openaiEfforts.push("xhigh") + } + return Object.fromEntries( + openaiEfforts.map((effort) => [ + effort, + { + reasoningEffort: effort, + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + ]), + ) case "@ai-sdk/anthropic": + // https://v5.ai-sdk.dev/providers/ai-sdk-providers/anthropic return { - thinking: { - type: "enabled", - budgetTokens: thinking.effort === "medium" ? 16000 : 31999, + high: { + thinking: { + type: "enabled", + budgetTokens: 16000, + }, + }, + max: { + thinking: { + type: "enabled", + budgetTokens: 31999, + }, }, } case "@ai-sdk/amazon-bedrock": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock - return { - reasoningConfig: { - type: "enabled", - maxReasoningEffort: thinking.effort, - }, - } + return Object.fromEntries( + WIDELY_SUPPORTED_EFFORTS.map((effort) => [ + effort, + { + reasoningConfig: { + type: "enabled", + maxReasoningEffort: effort, + }, + }, + ]), + ) case "@ai-sdk/google-vertex": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex @@ -310,42 +345,50 @@ export namespace ProviderTransform { // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai if (model.id.includes("2.5")) { return { - thinkingConfig: { - includeThoughts: true, - thinkingBudget: thinking.effort === "medium" ? 8192 : 24576, + high: { + thinkingConfig: { + includeThoughts: true, + thinkingBudget: 16000, + }, + }, + max: { + thinkingConfig: { + includeThoughts: true, + thinkingBudget: 24576, + }, }, } } - return { - thinkingConfig: { - includeThoughts: true, - thinkingLevel: thinking.effort, - }, - } + return Object.fromEntries( + ["low", "high"].map((effort) => [ + effort, + { + includeThoughts: true, + thinkingLevel: effort, + }, + ]), + ) case "@ai-sdk/mistral": - // TODO: implement mistral thinking options // https://v5.ai-sdk.dev/providers/ai-sdk-providers/mistral return {} case "@ai-sdk/cohere": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/cohere - const res: Record = { - thinking: { - type: "enabled", - }, - } - if (thinking.effort === "medium") { - res["thinking"]["budgetTokens"] = 8192 - } - return res + return {} case "@ai-sdk/groq": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/groq - return { - reasoningFormat: "parsed", - reasoningEffort: thinking.effort, - } + const groqEffort = ["none", ...WIDELY_SUPPORTED_EFFORTS] + return Object.fromEntries( + groqEffort.map((effort) => [ + effort, + { + includeThoughts: true, + thinkingLevel: effort, + }, + ]), + ) case "@ai-sdk/perplexity": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/perplexity @@ -366,7 +409,7 @@ export namespace ProviderTransform { include: true, } if (model.api.id.includes("gemini-3")) { - // result["reasoning"] = { effort: "high" } + result["reasoning"] = { effort: "high" } } } @@ -386,7 +429,7 @@ export namespace ProviderTransform { includeThoughts: true, } if (model.api.id.includes("gemini-3")) { - // result["thinkingConfig"]["thinkingLevel"] = "high" + result["thinkingConfig"]["thinkingLevel"] = "high" } } @@ -396,7 +439,7 @@ export namespace ProviderTransform { } if (!model.api.id.includes("codex") && !model.api.id.includes("gpt-5-pro")) { - // result["reasoningEffort"] = "medium" + result["reasoningEffort"] = "medium" } if (model.api.id.endsWith("gpt-5.") && model.providerID !== "azure") { diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 81cd3a4f550a..17193c9d49bc 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -287,9 +287,6 @@ export namespace MessageV2 { sessionID: z.string(), }) - export const Thinking = z.object({ effort: z.enum(["default", "medium", "high"]) }) - export type Thinking = z.infer - export const User = Base.extend({ role: z.literal("user"), time: z.object({ @@ -309,7 +306,7 @@ export namespace MessageV2 { }), system: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), - thinking: Thinking.optional(), + variant: z.record(z.string(), z.any()).optional(), }).meta({ ref: "UserMessage", }) From ca7734ac95cecaa12096e2bec9086af689684c6b Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 15:01:09 -0600 Subject: [PATCH 10/17] wip --- packages/opencode/package.json | 14 ++-- .../cli/cmd/tui/component/prompt/index.tsx | 40 +++++------ .../src/cli/cmd/tui/context/local.tsx | 71 +++++++++++++++---- packages/opencode/src/provider/provider.ts | 1 + packages/opencode/src/provider/transform.ts | 11 +-- packages/opencode/src/session/llm.ts | 3 +- packages/opencode/src/session/message-v2.ts | 2 +- packages/opencode/src/session/prompt.ts | 8 +-- packages/sdk/js/src/v2/gen/sdk.gen.ts | 18 ++--- packages/sdk/js/src/v2/gen/types.gen.ts | 28 ++++---- 10 files changed, 117 insertions(+), 79 deletions(-) diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e0459aa41c0f..bfd2e3cbad06 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -52,21 +52,21 @@ "@ai-sdk/amazon-bedrock": "3.0.57", "@ai-sdk/anthropic": "2.0.54", "@ai-sdk/azure": "2.0.82", + "@ai-sdk/cerebras": "1.0.33", + "@ai-sdk/cohere": "2.0.21", + "@ai-sdk/deepinfra": "1.0.30", + "@ai-sdk/gateway": "2.0.23", "@ai-sdk/google": "2.0.49", "@ai-sdk/google-vertex": "3.0.81", + "@ai-sdk/groq": "2.0.33", "@ai-sdk/mistral": "2.0.26", "@ai-sdk/openai": "2.0.71", "@ai-sdk/openai-compatible": "1.0.29", + "@ai-sdk/perplexity": "2.0.22", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", - "@ai-sdk/xai": "2.0.42", - "@ai-sdk/cerebras": "1.0.33", - "@ai-sdk/cohere": "2.0.21", - "@ai-sdk/deepinfra": "1.0.30", - "@ai-sdk/gateway": "2.0.23", - "@ai-sdk/groq": "2.0.33", - "@ai-sdk/perplexity": "2.0.22", "@ai-sdk/togetherai": "1.0.30", + "@ai-sdk/xai": "2.0.42", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 1fac566e9677..d971225544b0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -205,9 +205,9 @@ export function Prompt(props: PromptProps) { local.model.set(msg.model) } - // Set effort from last message - if (msg.thinking?.effort) { - local.effort.set(msg.thinking.effort) + // Set variant from last message + if (msg.variant) { + local.variant.set(msg.variant) } }) @@ -589,7 +589,7 @@ export function Prompt(props: PromptProps) { // Capture mode before it gets reset const currentMode = store.mode - const thinking = showEffort() ? { effort: local.effort.current() } : undefined + const variant = local.variant.current() if (store.mode === "shell") { sdk.client.session.shell({ @@ -618,7 +618,7 @@ export function Prompt(props: PromptProps) { agent: local.agent.current().name, model: `${selectedModel.providerID}/${selectedModel.modelID}`, messageID, - thinking, + variant, }) } else { sdk.client.session.prompt({ @@ -627,7 +627,7 @@ export function Prompt(props: PromptProps) { messageID, agent: local.agent.current().name, model: selectedModel, - thinking, + variant, parts: [ { id: Identifier.ascending("part"), @@ -748,19 +748,11 @@ export function Prompt(props: PromptProps) { return local.agent.color(local.agent.current().name) }) - const showEffort = createMemo(() => { - if (!local.model.parsed().reasoning) return false - if (local.effort.current() === "default") return false - const modelID = local.model.current()?.modelID?.toLowerCase() ?? "" - // until models.dev has better reasoning constructs, let's just blacklist models that cant have toggled reasoning - if ( - modelID.includes("deepseek") || - modelID.includes("minimax") || - modelID.includes("glm") || - modelID.includes("big-pickle") - ) - return false - return true + const showVariant = createMemo(() => { + const variants = local.variant.list() + if (variants.length === 0) return false + const current = local.variant.current() + return !!current }) const spinnerDef = createMemo(() => { @@ -888,10 +880,10 @@ export function Prompt(props: PromptProps) { return } } - if (keybind.match("effort_cycle", e)) { + if (keybind.match("variant_cycle", e)) { e.preventDefault() - if (!local.model.parsed().reasoning) return - local.effort.cycle() + if (local.variant.list().length === 0) return + local.variant.cycle() return } if (store.mode === "normal") autocomplete.onKeyDown(e) @@ -1009,10 +1001,10 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} {local.model.parsed().provider} - + · - {local.effort.current()} + {local.variant.current()} diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index d390ec41c045..a46baebd2087 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -51,25 +51,72 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } }) - const effort = iife(() => { - const [effortStore, setEffortStore] = createStore<{ - current: "default" | "medium" | "high" + const variant = iife(() => { + // Store variant selection per model (providerID/modelID) + const [variantStore, setVariantStore] = createStore<{ + model: Record }>({ - current: "default", + model: {}, }) + const file = Bun.file(path.join(Global.Path.state, "variant.json")) + + function save() { + Bun.write(file, JSON.stringify(variantStore.model)) + } + + file + .json() + .then((x) => { + if (typeof x === "object" && x !== null) { + setVariantStore("model", x) + } + }) + .catch(() => {}) + + function modelKey() { + const m = model.current() + return m ? `${m.providerID}/${m.modelID}` : undefined + } + + function list() { + const m = model.current() + if (!m) return [] + const provider = sync.data.provider.find((x) => x.id === m.providerID) + const info = provider?.models[m.modelID] + if (!info?.variants) return [] + return Object.entries(info.variants) + .filter(([_, v]) => !v.disabled) + .map(([name]) => name) + } + return { current() { - return effortStore.current + const key = modelKey() + return key ? variantStore.model[key] : undefined }, - set(value: "default" | "medium" | "high") { - setEffortStore("current", value) + list, + set(value: string | undefined) { + const key = modelKey() + if (!key) return + setVariantStore("model", key, value) + save() }, cycle() { - const levels: Array<"default" | "medium" | "high"> = ["default", "medium", "high"] - const currentIndex = levels.indexOf(effortStore.current) - const nextIndex = (currentIndex + 1) % levels.length - setEffortStore("current", levels[nextIndex]) + const variants = list() + if (variants.length === 0) return + const current = this.current() + if (!current) { + this.set(variants[0]) + return + } + const index = variants.indexOf(current) + if (index === -1 || index === variants.length - 1) { + // Not found or at end - clear selection + this.set(undefined) + return + } + this.set(variants[index + 1]) }, } }) @@ -358,7 +405,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ model, agent, mcp, - effort, + variant, } return result }, diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index b465e9166040..36592963e2d6 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -543,6 +543,7 @@ export namespace Provider { interleaved: model.interleaved ?? false, }, release_date: model.release_date, + variants: {}, } m.variants = mapValues(ProviderTransform.variants(m), (v) => ({ disabled: false, ...v })) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 5ffe250be8d8..d641b9e72073 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -250,6 +250,9 @@ export namespace ProviderTransform { export function variants(model: Provider.Model) { if (!model.capabilities.reasoning) return {} + const id = model.id.toLowerCase() + if (id.includes("deepseek") || id.includes("minimax") || id.includes("glm") || id.includes("mistral")) return {} + switch (model.api.npm) { case "@openrouter/ai-sdk-provider": if (!model.id.includes("gpt") && !model.id.includes("gemini-3") && !model.id.includes("grok-4")) return {} @@ -272,9 +275,9 @@ export namespace ProviderTransform { case "@ai-sdk/azure": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure - if (model.id === "o1-mini") return {} + if (id === "o1-mini") return {} const azureEfforts = ["low", "medium", "high"] - if (model.id.includes("gpt-5")) { + if (id.includes("gpt-5")) { azureEfforts.unshift("minimal") } return Object.fromEntries( @@ -289,7 +292,7 @@ export namespace ProviderTransform { ) case "@ai-sdk/openai": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/openai - if (model.id === "gpt-5-pro") return {} + if (id === "gpt-5-pro") return {} const openaiEfforts = ["minimal", ...WIDELY_SUPPORTED_EFFORTS] if (model.release_date >= "2025-11-13") { openaiEfforts.unshift("none") @@ -343,7 +346,7 @@ export namespace ProviderTransform { // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-vertex case "@ai-sdk/google": // https://v5.ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai - if (model.id.includes("2.5")) { + if (id.includes("2.5")) { return { high: { thinkingConfig: { diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 36be190458e2..a1461c315575 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -74,12 +74,13 @@ export namespace LLM { } const provider = await Provider.getProvider(input.model.providerID) + const variant = input.model.variants && input.user.variant ? input.model.variants[input.user.variant] : undefined const options = pipe( ProviderTransform.options(input.model, input.sessionID, provider.options), mergeDeep(input.small ? ProviderTransform.smallOptions(input.model) : {}), mergeDeep(input.model.options), mergeDeep(input.agent.options), - mergeDeep(input.user.thinking ? ProviderTransform.thinking(input.model, input.user.thinking) : {}), + mergeDeep(variant && !variant.disabled ? variant : {}), ) const params = await Plugin.trigger( diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 17193c9d49bc..e7e304e3a6ae 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -306,7 +306,7 @@ export namespace MessageV2 { }), system: z.string().optional(), tools: z.record(z.string(), z.boolean()).optional(), - variant: z.record(z.string(), z.any()).optional(), + variant: z.string().optional(), }).meta({ ref: "UserMessage", }) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 93dd690b9e90..595fc746e7f7 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -90,7 +90,7 @@ export namespace SessionPrompt { noReply: z.boolean().optional(), tools: z.record(z.string(), z.boolean()).optional(), system: z.string().optional(), - thinking: MessageV2.Thinking.optional(), + variant: z.string().optional(), parts: z.array( z.discriminatedUnion("type", [ MessageV2.TextPart.omit({ @@ -728,7 +728,7 @@ export namespace SessionPrompt { agent: agent.name, model: input.model ?? agent.model ?? (await lastModel(input.sessionID)), system: input.system, - thinking: input.thinking, + variant: input.variant, } const parts = await Promise.all( @@ -1269,7 +1269,7 @@ export namespace SessionPrompt { model: z.string().optional(), arguments: z.string(), command: z.string(), - thinking: MessageV2.Thinking.optional(), + variant: z.string().optional(), }) export type CommandInput = z.infer const bashRegex = /!`([^`]+)`/g @@ -1372,7 +1372,7 @@ export namespace SessionPrompt { model, agent: agentName, parts, - thinking: input.thinking, + variant: input.variant, })) as MessageV2.WithParts Bus.publish(Command.Event.Executed, { diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 2667d4e5542d..c01d351bafe0 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -1228,9 +1228,7 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string parts?: Array }, options?: Options, @@ -1248,7 +1246,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, - { in: "body", key: "thinking" }, + { in: "body", key: "variant" }, { in: "body", key: "parts" }, ], }, @@ -1318,9 +1316,7 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string parts?: Array }, options?: Options, @@ -1338,7 +1334,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, - { in: "body", key: "thinking" }, + { in: "body", key: "variant" }, { in: "body", key: "parts" }, ], }, @@ -1370,9 +1366,7 @@ export class Session extends HeyApiClient { model?: string arguments?: string command?: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string }, options?: Options, ) { @@ -1388,7 +1382,7 @@ export class Session extends HeyApiClient { { in: "body", key: "model" }, { in: "body", key: "arguments" }, { in: "body", key: "command" }, - { in: "body", key: "thinking" }, + { in: "body", key: "variant" }, ], }, ], diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index c93b4e19f251..8f4daa1439dc 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -90,9 +90,7 @@ export type UserMessage = { tools?: { [key: string]: boolean } - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string } export type ProviderAuthError = { @@ -973,9 +971,9 @@ export type KeybindsConfig = { */ agent_cycle_reverse?: string /** - * Cycle thinking effort level + * Cycle model variants */ - effort_cycle?: string + variant_cycle?: string /** * Clear input field */ @@ -1719,6 +1717,11 @@ export type Command = { subtask?: boolean } +export type Variant = { + disabled: boolean + [key: string]: unknown | boolean +} + export type Model = { id: string providerID: string @@ -1782,6 +1785,9 @@ export type Model = { [key: string]: string } release_date: string + variants?: { + [key: string]: Variant + } } export type Provider = { @@ -2951,9 +2957,7 @@ export type SessionPromptData = { [key: string]: boolean } system?: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string parts: Array } path: { @@ -3137,9 +3141,7 @@ export type SessionPromptAsyncData = { [key: string]: boolean } system?: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string parts: Array } path: { @@ -3183,9 +3185,7 @@ export type SessionCommandData = { model?: string arguments: string command: string - thinking?: { - effort: "default" | "medium" | "high" - } + variant?: string } path: { /** From cfb76da9624ed1a2b812a728abaff862c21d2d85 Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 29 Dec 2025 21:03:14 +0000 Subject: [PATCH 11/17] Update Nix flake.lock and hashes --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 6cb2cf919712..81a005ec3606 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1766870016, - "narHash": "sha256-fHmxAesa6XNqnIkcS6+nIHuEmgd/iZSP/VXxweiEuQw=", + "lastModified": 1766996594, + "narHash": "sha256-SosfgQSqVmOkqVgNYJnxW5FvoIQX4grOcpIKNrIwz4o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5c2bc52fb9f8c264ed6c93bd20afa2ff5e763dce", + "rev": "0744ef1b047f07d31d9962d757ffe38ec14a4d41", "type": "github" }, "original": { From 45cf3f27a5b2835cbb2283532a8689b4471ca708 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 15:14:17 -0600 Subject: [PATCH 12/17] wip --- .../cli/cmd/tui/component/prompt/index.tsx | 14 +- .../src/cli/cmd/tui/context/local.tsx | 151 +++++++----------- 2 files changed, 69 insertions(+), 96 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index d971225544b0..6eec5d1d6220 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -207,7 +207,7 @@ export function Prompt(props: PromptProps) { // Set variant from last message if (msg.variant) { - local.variant.set(msg.variant) + local.model.variant.set(msg.variant) } }) @@ -589,7 +589,7 @@ export function Prompt(props: PromptProps) { // Capture mode before it gets reset const currentMode = store.mode - const variant = local.variant.current() + const variant = local.model.variant.current() if (store.mode === "shell") { sdk.client.session.shell({ @@ -749,9 +749,9 @@ export function Prompt(props: PromptProps) { }) const showVariant = createMemo(() => { - const variants = local.variant.list() + const variants = local.model.variant.list() if (variants.length === 0) return false - const current = local.variant.current() + const current = local.model.variant.current() return !!current }) @@ -882,8 +882,8 @@ export function Prompt(props: PromptProps) { } if (keybind.match("variant_cycle", e)) { e.preventDefault() - if (local.variant.list().length === 0) return - local.variant.cycle() + if (local.model.variant.list().length === 0) return + local.model.variant.cycle() return } if (store.mode === "normal") autocomplete.onKeyDown(e) @@ -1004,7 +1004,7 @@ export function Prompt(props: PromptProps) { · - {local.variant.current()} + {local.model.variant.current()} diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index a46baebd2087..cac542d54fc5 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -33,94 +33,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } } - // Automatically update model when agent changes - createEffect(() => { - const value = agent.current() - if (value.model) { - if (isModelValid(value.model)) - model.set({ - providerID: value.model.providerID, - modelID: value.model.modelID, - }) - else - toast.show({ - variant: "warning", - message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`, - duration: 3000, - }) - } - }) - - const variant = iife(() => { - // Store variant selection per model (providerID/modelID) - const [variantStore, setVariantStore] = createStore<{ - model: Record - }>({ - model: {}, - }) - - const file = Bun.file(path.join(Global.Path.state, "variant.json")) - - function save() { - Bun.write(file, JSON.stringify(variantStore.model)) - } - - file - .json() - .then((x) => { - if (typeof x === "object" && x !== null) { - setVariantStore("model", x) - } - }) - .catch(() => {}) - - function modelKey() { - const m = model.current() - return m ? `${m.providerID}/${m.modelID}` : undefined - } - - function list() { - const m = model.current() - if (!m) return [] - const provider = sync.data.provider.find((x) => x.id === m.providerID) - const info = provider?.models[m.modelID] - if (!info?.variants) return [] - return Object.entries(info.variants) - .filter(([_, v]) => !v.disabled) - .map(([name]) => name) - } - - return { - current() { - const key = modelKey() - return key ? variantStore.model[key] : undefined - }, - list, - set(value: string | undefined) { - const key = modelKey() - if (!key) return - setVariantStore("model", key, value) - save() - }, - cycle() { - const variants = list() - if (variants.length === 0) return - const current = this.current() - if (!current) { - this.set(variants[0]) - return - } - const index = variants.indexOf(current) - if (index === -1 || index === variants.length - 1) { - // Not found or at end - clear selection - this.set(undefined) - return - } - this.set(variants[index + 1]) - }, - } - }) - const agent = iife(() => { const agents = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden)) const [agentStore, setAgentStore] = createStore<{ @@ -190,11 +102,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ providerID: string modelID: string }[] + variant: Record }>({ ready: false, model: {}, recent: [], favorite: [], + variant: {}, }) const file = Bun.file(path.join(Global.Path.state, "model.json")) @@ -205,6 +119,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ JSON.stringify({ recent: modelStore.recent, favorite: modelStore.favorite, + variant: modelStore.variant, }), ) } @@ -214,6 +129,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ .then((x) => { if (Array.isArray(x.recent)) setModelStore("recent", x.recent) if (Array.isArray(x.favorite)) setModelStore("favorite", x.favorite) + if (typeof x.variant === "object" && x.variant !== null) setModelStore("variant", x.variant) }) .catch(() => {}) .finally(() => { @@ -381,6 +297,46 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ save() }) }, + variant: { + current() { + const m = currentModel() + if (!m) return undefined + const key = `${m.providerID}/${m.modelID}` + return modelStore.variant[key] + }, + list() { + const m = currentModel() + if (!m) return [] + const provider = sync.data.provider.find((x) => x.id === m.providerID) + const info = provider?.models[m.modelID] + if (!info?.variants) return [] + return Object.entries(info.variants) + .filter(([_, v]) => !v.disabled) + .map(([name]) => name) + }, + set(value: string | undefined) { + const m = currentModel() + if (!m) return + const key = `${m.providerID}/${m.modelID}` + setModelStore("variant", key, value) + save() + }, + cycle() { + const variants = this.list() + if (variants.length === 0) return + const current = this.current() + if (!current) { + this.set(variants[0]) + return + } + const index = variants.indexOf(current) + if (index === -1 || index === variants.length - 1) { + this.set(undefined) + return + } + this.set(variants[index + 1]) + }, + }, } }) @@ -401,11 +357,28 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ }, } + // Automatically update model when agent changes + createEffect(() => { + const value = agent.current() + if (value.model) { + if (isModelValid(value.model)) + model.set({ + providerID: value.model.providerID, + modelID: value.model.modelID, + }) + else + toast.show({ + variant: "warning", + message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`, + duration: 3000, + }) + } + }) + const result = { model, agent, mcp, - variant, } return result }, From 4d2944d17cc3cc4d62b7d705b473bde529738e5d Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 15:19:47 -0600 Subject: [PATCH 13/17] test: bump timeout --- packages/opencode/bunfig.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/bunfig.toml b/packages/opencode/bunfig.toml index 9afe227b326b..c227328d5ae7 100644 --- a/packages/opencode/bunfig.toml +++ b/packages/opencode/bunfig.toml @@ -2,5 +2,6 @@ preload = ["@opentui/solid/preload"] [test] preload = ["./test/preload.ts"] +timeout = 10000 # 10 seconds (default is 5000ms) # Enable code coverage coverage = true From 0c0fff773acf971069fb277677b3571cefb51fd4 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 15:22:10 -0600 Subject: [PATCH 14/17] chore: rm dead import --- packages/opencode/src/provider/transform.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index d641b9e72073..20b3ec771526 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -3,7 +3,6 @@ import { unique } from "remeda" import type { JSONSchema } from "zod/v4/core" import type { Provider } from "./provider" import type { ModelsDev } from "./models" -import type { MessageV2 } from "@/session/message-v2" type Modality = NonNullable["input"][number] From d835924c304d0d067dcb635453dd549fa269f0ea Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Dec 2025 15:31:59 -0600 Subject: [PATCH 15/17] fix: lockfile --- bun.lock | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index ccc3be10b2cb..17b8a3117043 100644 --- a/bun.lock +++ b/bun.lock @@ -255,7 +255,7 @@ "@agentclientprotocol/sdk": "0.5.1", "@ai-sdk/amazon-bedrock": "3.0.57", "@ai-sdk/anthropic": "2.0.56", - "@ai-sdk/azure": "2.0.73", + "@ai-sdk/azure": "2.0.82", "@ai-sdk/cerebras": "1.0.33", "@ai-sdk/cohere": "2.0.21", "@ai-sdk/deepinfra": "1.0.30", @@ -3904,16 +3904,10 @@ "@ai-sdk/deepinfra/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], - "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="], - - "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], - "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="], "@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="], - "@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], - "@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="], "@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="], @@ -4910,8 +4904,6 @@ "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], - "opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], "opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="], From 3780fb50fb9f2f985a3860116df2035884fc1826 Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 29 Dec 2025 21:33:36 +0000 Subject: [PATCH 16/17] Update Nix flake.lock and hashes --- nix/hashes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/hashes.json b/nix/hashes.json index d801630a5802..bde5a2186f69 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-2i/QMBzp9MalOXur36mXaDDU8R9G/0dODCODEQOnaCU=" + "nodeModules": "sha256-nbA8oJETThGZOERzaq7NNzEIgqZg65eZMNYUKM2f2rc=" } From aa7da55c096fe0aff1392f3edaf18eb2e6d82d54 Mon Sep 17 00:00:00 2001 From: Github Action Date: Tue, 30 Dec 2025 03:28:58 +0000 Subject: [PATCH 17/17] Update Nix flake.lock and hashes --- nix/hashes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/hashes.json b/nix/hashes.json index bde5a2186f69..8c2a305e2a50 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-nbA8oJETThGZOERzaq7NNzEIgqZg65eZMNYUKM2f2rc=" + "nodeModules": "sha256-kgJKAqccRJOQcJ8+/j+lGe7T6WZuQqYv6UGl3jvI9wQ=" }