From 2806f240ea0ae56d5f46f6e7e377e9d732ad47d5 Mon Sep 17 00:00:00 2001 From: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Date: Wed, 24 Dec 2025 01:11:48 +0100 Subject: [PATCH 001/219] fix: resize textarea when pasting prompt less than 150 chars (#6070) --- .../opencode/src/cli/cmd/tui/component/prompt/index.tsx | 7 +++++++ 1 file changed, 7 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 18384f65e01..8972ba36e84 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -930,6 +930,13 @@ export function Prompt(props: PromptProps) { pasteText(pastedContent, `[Pasted ~${lineCount} lines]`) return } + + // Force layout update and render for the pasted content + setTimeout(() => { + input.getLayoutNode().markDirty() + input.gotoBufferEnd() + renderer.requestRender() + }, 0) }} ref={(r: TextareaRenderable) => { input = r From e8ac0b663b9c88c318a40571c2847f5f3fa0c5c7 Mon Sep 17 00:00:00 2001 From: rari404 <138394996+edlsh@users.noreply.github.com> Date: Tue, 23 Dec 2025 19:46:01 -0500 Subject: [PATCH 002/219] feat(tui): console copy-to-clipboard via opentui (#5658) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/cli/cmd/tui/app.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 5105ee3c639..13c95d9b9ea 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -179,6 +179,20 @@ function App() { const exit = useExit() const promptRef = usePromptRef() + // Wire up console copy-to-clipboard via opentui's onCopySelection callback + renderer.console.onCopySelection = async (text: string) => { + if (!text || text.length === 0) return + + const base64 = Buffer.from(text).toString("base64") + const osc52 = `\x1b]52;c;${base64}\x07` + const finalOsc52 = process.env["TMUX"] ? `\x1bPtmux;\x1b${osc52}\x1b\\` : osc52 + // @ts-expect-error writeOut is not in type definitions + renderer.writeOut(finalOsc52) + await Clipboard.copy(text) + .then(() => toast.show({ message: "Copied to clipboard", variant: "info" })) + .catch(toast.error) + renderer.clearSelection() + } const [terminalTitleEnabled, setTerminalTitleEnabled] = createSignal(kv.get("terminal_title_enabled", true)) createEffect(() => { @@ -447,7 +461,7 @@ function App() { { title: "Toggle console", category: "System", - value: "app.fps", + value: "app.console", onSelect: (dialog) => { renderer.console.toggle() dialog.clear() From d6c81d6e14b75c45492e1e673403ce421e837d0b Mon Sep 17 00:00:00 2001 From: David Hill <1879069+iamdavidhill@users.noreply.github.com> Date: Wed, 24 Dec 2025 00:57:02 +0000 Subject: [PATCH 003/219] style: update current todo style (#6077) --- packages/opencode/src/cli/cmd/tui/component/todo-item.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/todo-item.tsx b/packages/opencode/src/cli/cmd/tui/component/todo-item.tsx index 71c7db0fcbc..b54cc463341 100644 --- a/packages/opencode/src/cli/cmd/tui/component/todo-item.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/todo-item.tsx @@ -13,16 +13,16 @@ export function TodoItem(props: TodoItemProps) { - [{props.status === "completed" ? "✓" : " "}]{" "} + [{props.status === "completed" ? "✓" : props.status === "in_progress" ? "•" : " "}]{" "} {props.content} From 2730e0c9cde467c7ebc734ee632be8358f20a3a5 Mon Sep 17 00:00:00 2001 From: Rohan Mukherjee Date: Wed, 24 Dec 2025 06:34:44 +0530 Subject: [PATCH 004/219] chore: update AGENTS.md to ~150 lines (#5955) --- packages/opencode/src/command/template/initialize.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/command/template/initialize.txt b/packages/opencode/src/command/template/initialize.txt index 5bb59c36c0d..3f906b1eb83 100644 --- a/packages/opencode/src/command/template/initialize.txt +++ b/packages/opencode/src/command/template/initialize.txt @@ -2,7 +2,7 @@ Please analyze this codebase and create an AGENTS.md file containing: 1. Build/lint/test commands - especially for running a single test 2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc. -The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long. +The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 150 lines long. If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them. If there's already an AGENTS.md, improve it if it's located in ${path} From 12ee9d51c375b2cb30657c40a234b43d01683a9b Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 23 Dec 2025 19:20:31 -0600 Subject: [PATCH 005/219] make 'The socket connection was closed unexpectedly' errors retryable --- packages/opencode/src/session/message-v2.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 14542669ee9..afdef9e89a3 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -12,6 +12,7 @@ import { Storage } from "@/storage/storage" import { ProviderTransform } from "@/provider/transform" import { STATUS_CODES } from "http" import { iife } from "@/util/iife" +import { type SystemError } from "bun" export namespace MessageV2 { export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({})) @@ -31,6 +32,7 @@ export namespace MessageV2 { isRetryable: z.boolean(), responseHeaders: z.record(z.string(), z.string()).optional(), responseBody: z.string().optional(), + details: z.record(z.string(), z.string()).optional(), }), ) export type APIError = z.infer @@ -609,6 +611,20 @@ export namespace MessageV2 { }, { cause: e }, ).toObject() + case (e as SystemError)?.code === "ECONNRESET" && + (e as SystemError)?.message.includes("The socket connection was closed unexpectedly"): + return new MessageV2.APIError( + { + message: "Connection reset by server", + isRetryable: true, + details: { + code: (e as SystemError).code ?? "", + syscall: (e as SystemError).syscall ?? "", + message: (e as SystemError).message ?? "", + }, + }, + { cause: e }, + ).toObject() case APICallError.isInstance(e): const message = iife(() => { let msg = e.message From f9b5ce180a2799d6d43e072cccc61f10d7bbfe1b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 24 Dec 2025 01:21:10 +0000 Subject: [PATCH 006/219] chore: generate --- packages/sdk/js/src/v2/gen/types.gen.ts | 3 +++ packages/sdk/openapi.json | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 0021ca1f5d3..7f31018c62f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -131,6 +131,9 @@ export type ApiError = { [key: string]: string } responseBody?: string + details?: { + [key: string]: string + } } } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 10dd7365ceb..b05aa786df9 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5336,6 +5336,15 @@ }, "responseBody": { "type": "string" + }, + "details": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } } }, "required": ["message", "isRetryable"] From 7396d495ee172c8705bce3bce0f71833c7ce56bd Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 23 Dec 2025 19:22:38 -0600 Subject: [PATCH 007/219] chore: regen sdk --- packages/opencode/src/session/message-v2.ts | 4 ++-- packages/sdk/js/src/v2/gen/types.gen.ts | 2 +- packages/sdk/openapi.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index afdef9e89a3..22b276dd03c 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -32,7 +32,7 @@ export namespace MessageV2 { isRetryable: z.boolean(), responseHeaders: z.record(z.string(), z.string()).optional(), responseBody: z.string().optional(), - details: z.record(z.string(), z.string()).optional(), + metadata: z.record(z.string(), z.string()).optional(), }), ) export type APIError = z.infer @@ -617,7 +617,7 @@ export namespace MessageV2 { { message: "Connection reset by server", isRetryable: true, - details: { + metadata: { code: (e as SystemError).code ?? "", syscall: (e as SystemError).syscall ?? "", message: (e as SystemError).message ?? "", diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 7f31018c62f..2a318fed527 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -131,7 +131,7 @@ export type ApiError = { [key: string]: string } responseBody?: string - details?: { + metadata?: { [key: string]: string } } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index b05aa786df9..7c965446af2 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5337,7 +5337,7 @@ "responseBody": { "type": "string" }, - "details": { + "metadata": { "type": "object", "propertyNames": { "type": "string" From 8b40e38cd7f03fa7141f21020a971a6030673cc9 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 23 Dec 2025 19:28:41 -0600 Subject: [PATCH 008/219] test: add test for retry --- packages/opencode/test/session/retry.test.ts | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index b685eae95df..dc29be0b768 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -59,3 +59,53 @@ describe("session.retry.delay", () => { expect(SessionRetry.delay(1, longError)).toBe(700000) }) }) + +describe("session.message-v2.fromError", () => { + test.concurrent( + "converts ECONNRESET socket errors to retryable APIError", + async () => { + using server = Bun.serve({ + port: 0, + idleTimeout: 8, + async fetch(req) { + return new Response( + new ReadableStream({ + async pull(controller) { + controller.enqueue("Hello,") + await Bun.sleep(10000) + controller.enqueue(" World!") + controller.close() + }, + }), + { headers: { "Content-Type": "text/plain" } }, + ) + }, + }) + + const error = await fetch(new URL("/", server.url.origin)) + .then((res) => res.text()) + .catch((e) => e) + + const result = MessageV2.fromError(error, { providerID: "test" }) + + expect(MessageV2.APIError.isInstance(result)).toBe(true) + expect((result as MessageV2.APIError).data.isRetryable).toBe(true) + expect((result as MessageV2.APIError).data.message).toBe("Connection reset by server") + expect((result as MessageV2.APIError).data.metadata?.code).toBe("ECONNRESET") + expect((result as MessageV2.APIError).data.metadata?.message).toInclude("socket connection") + }, + 15_000, + ) + + test("ECONNRESET socket error is retryable", () => { + const error = new MessageV2.APIError({ + message: "Connection reset by server", + isRetryable: true, + metadata: { code: "ECONNRESET", message: "The socket connection was closed unexpectedly" }, + }).toObject() as MessageV2.APIError + + const retryable = SessionRetry.retryable(error) + expect(retryable).toBeDefined() + expect(retryable).toBe("Connection reset by server") + }) +}) From f4d61be8bdd857bd85481a787fc754dfa766fe92 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 24 Dec 2025 02:36:37 +0100 Subject: [PATCH 009/219] feat(mcp): handle tools/list_changed notifications (#5913) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline --- packages/opencode/src/mcp/index.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 40ee2556520..88886c4ff4a 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -4,7 +4,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" -import type { Tool as MCPToolDef } from "@modelcontextprotocol/sdk/types.js" +import { type Tool as MCPToolDef, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js" import { Config } from "../config/config" import { Log } from "../util/log" import { NamedError } from "@opencode-ai/util/error" @@ -15,6 +15,7 @@ import { withTimeout } from "@/util/timeout" import { McpOAuthProvider } from "./oauth-provider" import { McpOAuthCallback } from "./oauth-callback" import { McpAuth } from "./auth" +import { BusEvent } from "../bus/bus-event" import { Bus } from "@/bus" import { TuiEvent } from "@/cli/cmd/tui/event" import open from "open" @@ -22,6 +23,13 @@ import open from "open" export namespace MCP { const log = Log.create({ service: "mcp" }) + export const ToolsChanged = BusEvent.define( + "mcp.tools.changed", + z.object({ + server: z.string(), + }), + ) + export const Failed = NamedError.create( "MCPFailed", z.object({ @@ -76,6 +84,14 @@ export namespace MCP { }) export type Status = z.infer + // Register notification handlers for MCP client + function registerNotificationHandlers(client: MCPClient, serverName: string) { + client.setNotificationHandler(ToolListChangedNotificationSchema, async () => { + log.info("tools list changed notification received", { server: serverName }) + Bus.publish(ToolsChanged, { server: serverName }) + }) + } + // Convert MCP tool definition to AI SDK Tool type function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient): Tool { const inputSchema = mcpTool.inputSchema @@ -236,6 +252,7 @@ export namespace MCP { version: Installation.VERSION, }) await client.connect(transport) + registerNotificationHandlers(client, key) mcpClient = client log.info("connected", { key, transport: name }) status = { status: "connected" } @@ -308,6 +325,7 @@ export namespace MCP { version: Installation.VERSION, }) await client.connect(transport) + registerNotificationHandlers(client, key) mcpClient = client status = { status: "connected", From 1a9ee3080cb401ad72a5042d4b803de868c0ac9f Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 23 Dec 2025 20:36:52 -0500 Subject: [PATCH 010/219] zen: sync --- infra/console.ts | 1 + packages/console/core/script/promote-models.ts | 5 ++++- packages/console/core/script/pull-models.ts | 6 ++++-- packages/console/core/script/update-models.ts | 12 +++++++++--- packages/console/core/src/model.ts | 3 ++- packages/console/core/sst-env.d.ts | 4 ++++ packages/console/function/sst-env.d.ts | 4 ++++ packages/console/resource/sst-env.d.ts | 4 ++++ packages/enterprise/sst-env.d.ts | 4 ++++ packages/function/sst-env.d.ts | 4 ++++ sst-env.d.ts | 4 ++++ 11 files changed, 44 insertions(+), 7 deletions(-) diff --git a/infra/console.ts b/infra/console.ts index 0cc6a404bd3..c69a706838e 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -103,6 +103,7 @@ const ZEN_MODELS = [ new sst.Secret("ZEN_MODELS3"), new sst.Secret("ZEN_MODELS4"), new sst.Secret("ZEN_MODELS5"), + new sst.Secret("ZEN_MODELS6"), ] const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { diff --git a/packages/console/core/script/promote-models.ts b/packages/console/core/script/promote-models.ts index bebef5cfb55..766fe6604c5 100755 --- a/packages/console/core/script/promote-models.ts +++ b/packages/console/core/script/promote-models.ts @@ -17,14 +17,16 @@ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[ const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] +const value6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1] if (!value1) throw new Error("ZEN_MODELS1 not found") if (!value2) throw new Error("ZEN_MODELS2 not found") if (!value3) throw new Error("ZEN_MODELS3 not found") if (!value4) throw new Error("ZEN_MODELS4 not found") if (!value5) throw new Error("ZEN_MODELS5 not found") +if (!value6) throw new Error("ZEN_MODELS6 not found") // validate value -ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5)) +ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5 + value6)) // update the secret await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}` @@ -32,3 +34,4 @@ await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}` await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}` await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}` await $`bun sst secret set ZEN_MODELS5 ${value5} --stage ${stage}` +await $`bun sst secret set ZEN_MODELS6 ${value6} --stage ${stage}` diff --git a/packages/console/core/script/pull-models.ts b/packages/console/core/script/pull-models.ts index afa865625b0..80b1037b68e 100755 --- a/packages/console/core/script/pull-models.ts +++ b/packages/console/core/script/pull-models.ts @@ -17,14 +17,15 @@ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[ const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] +const value6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1] if (!value1) throw new Error("ZEN_MODELS1 not found") if (!value2) throw new Error("ZEN_MODELS2 not found") if (!value3) throw new Error("ZEN_MODELS3 not found") if (!value4) throw new Error("ZEN_MODELS4 not found") if (!value5) throw new Error("ZEN_MODELS5 not found") - +if (!value6) throw new Error("ZEN_MODELS6 not found") // validate value -ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5)) +ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5 + value6)) // update the secret await $`bun sst secret set ZEN_MODELS1 ${value1}` @@ -32,3 +33,4 @@ await $`bun sst secret set ZEN_MODELS2 ${value2}` await $`bun sst secret set ZEN_MODELS3 ${value3}` await $`bun sst secret set ZEN_MODELS4 ${value4}` await $`bun sst secret set ZEN_MODELS5 ${value5}` +await $`bun sst secret set ZEN_MODELS6 ${value6}` diff --git a/packages/console/core/script/update-models.ts b/packages/console/core/script/update-models.ts index 5d40b4d5a3e..7872e2330fd 100755 --- a/packages/console/core/script/update-models.ts +++ b/packages/console/core/script/update-models.ts @@ -15,16 +15,20 @@ const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("= const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] const oldValue5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] +const oldValue6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1] if (!oldValue1) throw new Error("ZEN_MODELS1 not found") if (!oldValue2) throw new Error("ZEN_MODELS2 not found") if (!oldValue3) throw new Error("ZEN_MODELS3 not found") if (!oldValue4) throw new Error("ZEN_MODELS4 not found") if (!oldValue5) throw new Error("ZEN_MODELS5 not found") +if (!oldValue6) throw new Error("ZEN_MODELS6 not found") // store the prettified json to a temp file const filename = `models-${Date.now()}.json` const tempFile = Bun.file(path.join(os.tmpdir(), filename)) -await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5), null, 2)) +await tempFile.write( + JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5 + oldValue6), null, 2), +) console.log("tempFile", tempFile.name) // open temp file in vim and read the file on close @@ -33,15 +37,17 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text())) ZenData.validate(JSON.parse(newValue)) // update the secret -const chunk = Math.ceil(newValue.length / 5) +const chunk = Math.ceil(newValue.length / 6) const newValue1 = newValue.slice(0, chunk) const newValue2 = newValue.slice(chunk, chunk * 2) const newValue3 = newValue.slice(chunk * 2, chunk * 3) const newValue4 = newValue.slice(chunk * 3, chunk * 4) -const newValue5 = newValue.slice(chunk * 4) +const newValue5 = newValue.slice(chunk * 4, chunk * 5) +const newValue6 = newValue.slice(chunk * 5) await $`bun sst secret set ZEN_MODELS1 ${newValue1}` await $`bun sst secret set ZEN_MODELS2 ${newValue2}` await $`bun sst secret set ZEN_MODELS3 ${newValue3}` await $`bun sst secret set ZEN_MODELS4 ${newValue4}` await $`bun sst secret set ZEN_MODELS5 ${newValue5}` +await $`bun sst secret set ZEN_MODELS6 ${newValue6}` diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts index 55d6c895c58..5a4a6e3b6f0 100644 --- a/packages/console/core/src/model.ts +++ b/packages/console/core/src/model.ts @@ -72,7 +72,8 @@ export namespace ZenData { Resource.ZEN_MODELS2.value + Resource.ZEN_MODELS3.value + Resource.ZEN_MODELS4.value + - Resource.ZEN_MODELS5.value, + Resource.ZEN_MODELS5.value + + Resource.ZEN_MODELS6.value, ) return ModelsSchema.parse(json) }) diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index fd971c8fd07..ff2c614b3ac 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -118,6 +118,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index fd971c8fd07..ff2c614b3ac 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -118,6 +118,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index fd971c8fd07..ff2c614b3ac 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -118,6 +118,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index fd971c8fd07..ff2c614b3ac 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -118,6 +118,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index fd971c8fd07..ff2c614b3ac 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -118,6 +118,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/sst-env.d.ts b/sst-env.d.ts index efc287aa97c..d7ef06895d9 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -144,6 +144,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS6": { + "type": "sst.sst.Secret" + "value": string + } "ZenData": { "name": string "type": "sst.cloudflare.Bucket" From 4b6575999d6548b0e8048b3950bafedaeba62e3b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 24 Dec 2025 01:37:35 +0000 Subject: [PATCH 011/219] chore: generate --- packages/sdk/js/src/v2/gen/types.gen.ts | 8 ++++++++ packages/sdk/openapi.json | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 2a318fed527..0a31394ed9c 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -592,6 +592,13 @@ export type EventTuiToastShow = { } } +export type EventMcpToolsChanged = { + type: "mcp.tools.changed" + properties: { + server: string + } +} + export type EventCommandExecuted = { type: "command.executed" properties: { @@ -758,6 +765,7 @@ export type Event = | EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow + | EventMcpToolsChanged | EventCommandExecuted | EventSessionCreated | EventSessionUpdated diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 7c965446af2..96ba0720c73 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -6635,6 +6635,25 @@ }, "required": ["type", "properties"] }, + "Event.mcp.tools.changed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "mcp.tools.changed" + }, + "properties": { + "type": "object", + "properties": { + "server": { + "type": "string" + } + }, + "required": ["server"] + } + }, + "required": ["type", "properties"] + }, "Event.command.executed": { "type": "object", "properties": { @@ -7132,6 +7151,9 @@ { "$ref": "#/components/schemas/Event.tui.toast.show" }, + { + "$ref": "#/components/schemas/Event.mcp.tools.changed" + }, { "$ref": "#/components/schemas/Event.command.executed" }, From 99e211280764a59e6f28578b20bba794ee82797c Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 23 Dec 2025 22:10:23 -0600 Subject: [PATCH 012/219] tweak: retry err --- packages/opencode/src/session/message-v2.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 22b276dd03c..95bc3812e7a 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -611,8 +611,7 @@ export namespace MessageV2 { }, { cause: e }, ).toObject() - case (e as SystemError)?.code === "ECONNRESET" && - (e as SystemError)?.message.includes("The socket connection was closed unexpectedly"): + case (e as SystemError)?.code === "ECONNRESET": return new MessageV2.APIError( { message: "Connection reset by server", From 2c5c1ecb5e4bb453daf27b64bd8c199e6b3078aa Mon Sep 17 00:00:00 2001 From: xiantang Date: Wed, 24 Dec 2025 12:17:34 +0800 Subject: [PATCH 013/219] docs: add Neovim to the list of editors (#6081) --- packages/web/src/content/docs/tui.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/content/docs/tui.mdx b/packages/web/src/content/docs/tui.mdx index 83b5cd79869..023d9a3a883 100644 --- a/packages/web/src/content/docs/tui.mdx +++ b/packages/web/src/content/docs/tui.mdx @@ -325,6 +325,7 @@ Popular editor options include: - `code` - Visual Studio Code - `cursor` - Cursor - `windsurf` - Windsurf +- `nvim` - Neovim editor - `vim` - Vim editor - `nano` - Nano editor - `notepad` - Windows Notepad From 09d2febe278ffe49ca6c22b9eca96cc99a6f6f9a Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Tue, 23 Dec 2025 22:25:55 -0600 Subject: [PATCH 014/219] docs: skill tool/perm + parent keybind (#6001) Co-authored-by: opencode-agent[bot] Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/web/src/content/docs/keybinds.mdx | 5 ++- packages/web/src/content/docs/permissions.mdx | 37 ++++++++++++++++++- packages/web/src/content/docs/tools.mdx | 17 +++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/web/src/content/docs/keybinds.mdx b/packages/web/src/content/docs/keybinds.mdx index b7e370825d8..4f1b09d3f1b 100644 --- a/packages/web/src/content/docs/keybinds.mdx +++ b/packages/web/src/content/docs/keybinds.mdx @@ -24,8 +24,9 @@ OpenCode has a list of keybinds that you can customize through the OpenCode conf "session_unshare": "none", "session_interrupt": "escape", "session_compact": "c", - "session_child_cycle": "+right", - "session_child_cycle_reverse": "+left", + "session_child_cycle": "right", + "session_child_cycle_reverse": "left", + "session_parent": "up", "messages_page_up": "pageup", "messages_page_down": "pagedown", "messages_half_page_up": "ctrl+alt+u", diff --git a/packages/web/src/content/docs/permissions.mdx b/packages/web/src/content/docs/permissions.mdx index 1aea3ef740d..754a6c875dd 100644 --- a/packages/web/src/content/docs/permissions.mdx +++ b/packages/web/src/content/docs/permissions.mdx @@ -11,6 +11,7 @@ By default, OpenCode allows most operations without approval, except `doom_loop` "permission": { "edit": "allow", "bash": "ask", + "skill": "ask", "webfetch": "deny", "doom_loop": "ask", "external_directory": "ask" @@ -18,7 +19,7 @@ By default, OpenCode allows most operations without approval, except `doom_loop` } ``` -This lets you configure granular controls for the `edit`, `bash`, `webfetch`, `doom_loop`, and `external_directory` tools. +This lets you configure granular controls for the `edit`, `bash`, `skill`, `webfetch`, `doom_loop`, and `external_directory` tools. - `"ask"` — Prompt for approval before running the tool - `"allow"` — Allow all operations without approval @@ -28,7 +29,7 @@ This lets you configure granular controls for the `edit`, `bash`, `webfetch`, `d ## Tools -Currently, the permissions for the `edit`, `bash`, `webfetch`, `doom_loop`, and `external_directory` tools can be configured through the `permission` option. +Currently, the permissions for the `edit`, `bash`, `skill`, `webfetch`, `doom_loop`, and `external_directory` tools can be configured through the `permission` option. --- @@ -144,6 +145,38 @@ When an agent asks for permission to run a command in a pipeline, we use tree si --- +### skill + +Use the `permission.skill` key to control whether the model can load skills via the built-in `skill` tool. + +You can apply a single rule to all skills: + +```json title="opencode.json" {4} +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "skill": "ask" + } +} +``` + +Or configure per-skill rules (supports the same wildcard patterns as `permission.bash`): + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "skill": { + "*": "deny", + "git-*": "allow", + "frontend/*": "ask" + } + } +} +``` + +--- + ### webfetch Use the `permission.webfetch` key to control whether the LLM can fetch web pages. diff --git a/packages/web/src/content/docs/tools.mdx b/packages/web/src/content/docs/tools.mdx index dd678569e9a..e2f341ead49 100644 --- a/packages/web/src/content/docs/tools.mdx +++ b/packages/web/src/content/docs/tools.mdx @@ -230,6 +230,23 @@ This tool applies patch files to your codebase. Useful for applying diffs and pa --- +### skill + +Load a [skill](/docs/skills) (a `SKILL.md` file) and return its content in the conversation. + +```json title="opencode.json" {4} +{ + "$schema": "https://opencode.ai/config.json", + "tools": { + "skill": true + } +} +``` + +You can control approval prompts for loading skills via [permissions](/docs/permissions) using `permission.skill`. + +--- + ### todowrite Manage todo lists during coding sessions. From 6097d6af862002a559cdc0c8ffa5e69f5f746406 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Tue, 23 Dec 2025 22:37:49 -0600 Subject: [PATCH 015/219] docs: experimental LSP tool (#5943) Co-authored-by: opencode-agent[bot] Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline --- packages/web/src/content/docs/cli.mdx | 1 + packages/web/src/content/docs/lsp.mdx | 28 +++++++++++++++++++++++++ packages/web/src/content/docs/tools.mdx | 23 ++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/packages/web/src/content/docs/cli.mdx b/packages/web/src/content/docs/cli.mdx index c13712d1001..f104bfa0031 100644 --- a/packages/web/src/content/docs/cli.mdx +++ b/packages/web/src/content/docs/cli.mdx @@ -329,3 +329,4 @@ These environment variables enable experimental features that may change or be r | `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | Max output tokens for LLM responses | | `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | Enable file watcher for entire dir | | `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | Enable oxfmt formatter | +| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | Enable experimental LSP tool | diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index 230f782d313..0b79fa32957 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -51,6 +51,34 @@ You can disable automatic LSP server downloads by setting the `OPENCODE_DISABLE_ --- +## LLM Tool (Experimental) + +OpenCode includes an experimental built-in tool named `lsp` that lets the LLM query your LSP servers for code navigation and symbol information. + +To enable it, set `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` (or `OPENCODE_EXPERIMENTAL=true` to enable all experimental features). + +Once enabled, you can still disable it via your `tools` config: + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json", + "tools": { + "lsp": false + } +} +``` + +Example prompts: + +- "Use the lsp tool to go to definition of `createServer` in `src/server.ts` at line 12, character 5." +- "Use lsp hover on `User` in `src/models/user.ts` at line 20, character 10." + +:::note +The `lsp` tool only works when an LSP server is available for the file type. +::: + +--- + ## How It Works When opencode opens a file, it: diff --git a/packages/web/src/content/docs/tools.mdx b/packages/web/src/content/docs/tools.mdx index e2f341ead49..f1f84514378 100644 --- a/packages/web/src/content/docs/tools.mdx +++ b/packages/web/src/content/docs/tools.mdx @@ -213,6 +213,29 @@ This tool lists directory contents. It accepts glob patterns to filter results. --- +### lsp (experimental) + +Interact with your configured LSP servers to get code intelligence features like definitions, references, hover info, and call hierarchy. + +:::note +This tool is only available when `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` (or `OPENCODE_EXPERIMENTAL=true`). +::: + +```json title="opencode.json" {4} +{ + "$schema": "https://opencode.ai/config.json", + "tools": { + "lsp": true + } +} +``` + +Supported operations include `goToDefinition`, `findReferences`, `hover`, `documentSymbol`, `workspaceSymbol`, `goToImplementation`, `prepareCallHierarchy`, `incomingCalls`, and `outgoingCalls`. + +To configure which LSP servers are available for your project, see [LSP Servers](/docs/lsp). + +--- + ### patch Apply patches to files. From 4275907df68ca8ef99841b118fa4612ccfda7532 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 23 Dec 2025 22:38:17 -0600 Subject: [PATCH 016/219] docs: tweak lsp.mdx --- packages/web/src/content/docs/lsp.mdx | 28 --------------------------- 1 file changed, 28 deletions(-) diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index 0b79fa32957..230f782d313 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -51,34 +51,6 @@ You can disable automatic LSP server downloads by setting the `OPENCODE_DISABLE_ --- -## LLM Tool (Experimental) - -OpenCode includes an experimental built-in tool named `lsp` that lets the LLM query your LSP servers for code navigation and symbol information. - -To enable it, set `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` (or `OPENCODE_EXPERIMENTAL=true` to enable all experimental features). - -Once enabled, you can still disable it via your `tools` config: - -```json title="opencode.json" -{ - "$schema": "https://opencode.ai/config.json", - "tools": { - "lsp": false - } -} -``` - -Example prompts: - -- "Use the lsp tool to go to definition of `createServer` in `src/server.ts` at line 12, character 5." -- "Use lsp hover on `User` in `src/models/user.ts` at line 20, character 10." - -:::note -The `lsp` tool only works when an LSP server is available for the file type. -::: - ---- - ## How It Works When opencode opens a file, it: From 9e96d83164b8795625fed173579995fa256a72d3 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Wed, 24 Dec 2025 06:17:13 -0500 Subject: [PATCH 017/219] fix: remove SVG favicon to improve SEO (#5755) --- packages/ui/src/components/favicon.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui/src/components/favicon.tsx b/packages/ui/src/components/favicon.tsx index dec18f1adfc..3462384d458 100644 --- a/packages/ui/src/components/favicon.tsx +++ b/packages/ui/src/components/favicon.tsx @@ -4,7 +4,6 @@ export const Favicon = () => { return ( <> - From 3aca9e5fa5909ba3fef3e6908eeb01a898316744 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 24 Dec 2025 05:22:21 -0600 Subject: [PATCH 018/219] fix(desktop): conditionally show review pane toggle --- packages/app/src/components/header.tsx | 60 +++++++++++++------------- packages/app/src/pages/session.tsx | 3 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/packages/app/src/components/header.tsx b/packages/app/src/components/header.tsx index ec7cdfa25ba..3eae0e05d41 100644 --- a/packages/app/src/components/header.tsx +++ b/packages/app/src/components/header.tsx @@ -109,35 +109,37 @@ export function Header(props: {
-
- } - > - - + + +