From 1bce898ca72d275f3b4314e9b78d40f8a24d5f52 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:01:55 -0600 Subject: [PATCH 001/114] fix(desktop): file loading errors --- packages/desktop/src/context/local.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index f569738354..69807a2f44 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -360,7 +360,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const init = async (path: string) => { const relativePath = relative(path) if (!store.node[relativePath]) await fetch(path) - if (store.node[relativePath].loaded) return + if (store.node[relativePath]?.loaded) return return load(relativePath) } @@ -380,7 +380,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ context.addActive() if (options?.pinned) setStore("node", path, "pinned", true) if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view) - if (store.node[relativePath].loaded) return + if (store.node[relativePath]?.loaded) return return load(relativePath) } From 184643f0db48fe35dfbb32f5234bd8d28691c234 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:06:10 -0600 Subject: [PATCH 002/114] fix(desktop): non-latin file paths failed --- packages/util/src/encode.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/util/src/encode.ts b/packages/util/src/encode.ts index cc40fbe9d0..fc1f783bf2 100644 --- a/packages/util/src/encode.ts +++ b/packages/util/src/encode.ts @@ -1,9 +1,13 @@ export function base64Encode(value: string) { - return btoa(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "") + const bytes = new TextEncoder().encode(value) + const binary = Array.from(bytes, (b) => String.fromCharCode(b)).join("") + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "") } export function base64Decode(value: string) { - return atob(value.replace(/-/g, "+").replace(/_/g, "/")) + const binary = atob(value.replace(/-/g, "+").replace(/_/g, "/")) + const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0)) + return new TextDecoder().decode(bytes) } export async function hash(content: string, algorithm = "SHA-256"): Promise { From bf663905579d198d63ff1641112ec4f682cee09a Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:11:09 -0600 Subject: [PATCH 003/114] fix(desktop): better error reporting --- packages/desktop/src/pages/error.tsx | 30 +++++++++++++++++++++++----- packages/desktop/vite.config.ts | 1 + 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/desktop/src/pages/error.tsx b/packages/desktop/src/pages/error.tsx index 352b9f3e8e..c7330c2988 100644 --- a/packages/desktop/src/pages/error.tsx +++ b/packages/desktop/src/pages/error.tsx @@ -62,12 +62,32 @@ function formatInitError(error: InitError): string { } } -function formatError(error: unknown): string { +function formatErrorChain(error: unknown, depth = 0): string { if (!error) return "Unknown error" - if (isInitError(error)) return formatInitError(error) - if (error instanceof Error) return `${error.name}: ${error.message}\n\n${error.stack}` - if (typeof error === "string") return error - return JSON.stringify(error, null, 2) + + const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + + if (isInitError(error)) { + return indent + formatInitError(error) + } + + if (error instanceof Error) { + const parts = [indent + `${error.name}: ${error.message}`] + if (error.stack) { + parts.push(error.stack) + } + if (error.cause) { + parts.push(formatErrorChain(error.cause, depth + 1)) + } + return parts.join("\n\n") + } + + if (typeof error === "string") return indent + error + return indent + JSON.stringify(error, null, 2) +} + +function formatError(error: unknown): string { + return formatErrorChain(error, 0) } interface ErrorPageProps { diff --git a/packages/desktop/vite.config.ts b/packages/desktop/vite.config.ts index a388884cd5..57071a894f 100644 --- a/packages/desktop/vite.config.ts +++ b/packages/desktop/vite.config.ts @@ -10,5 +10,6 @@ export default defineConfig({ }, build: { target: "esnext", + sourcemap: true, }, }) From 9d48fd4bbdd74770bf4d8a59a476b1bbc0b9d817 Mon Sep 17 00:00:00 2001 From: opencode Date: Sun, 21 Dec 2025 10:14:41 +0000 Subject: [PATCH 004/114] release: v1.0.183 --- bun.lock | 30 +++++++++++++------------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++------ packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/tauri/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index 8027d29f01..0a3252e411 100644 --- a/bun.lock +++ b/bun.lock @@ -20,7 +20,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -48,7 +48,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -75,7 +75,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -99,7 +99,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -123,7 +123,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -171,7 +171,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -200,7 +200,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -216,7 +216,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.182", + "version": "1.0.183", "bin": { "opencode": "./bin/opencode", }, @@ -308,7 +308,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -328,7 +328,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.182", + "version": "1.0.183", "devDependencies": { "@hey-api/openapi-ts": "0.88.1", "@tsconfig/node22": "catalog:", @@ -339,7 +339,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -352,7 +352,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@opencode-ai/desktop": "workspace:*", "@solid-primitives/storage": "catalog:", @@ -379,7 +379,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -414,7 +414,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "zod": "catalog:", }, @@ -425,7 +425,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index e13b96f38c..515aff9035 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.0.182", + "version": "1.0.183", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 6da120dc92..3eadd2e4e9 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.182", + "version": "1.0.183", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 7ae216136d..c14a107ffa 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.182", + "version": "1.0.183", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 830400591a..095dd6f256 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.182", + "version": "1.0.183", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a41ed60c32..d70f13add9 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.182", + "version": "1.0.183", "description": "", "type": "module", "exports": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 54ce9e17f1..bad614e416 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.182", + "version": "1.0.183", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 8e7f26f1e5..dfd7bdffe6 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.0.182" +version = "1.0.183" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.182/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.183/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.182/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.183/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.182/opencode-linux-arm64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.183/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.182/opencode-linux-x64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.183/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.182/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.183/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 6f0fa372c4..06a5a51ad6 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.182", + "version": "1.0.183", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 2592553e75..321a466ee9 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.182", + "version": "1.0.183", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index a4753726dc..7a6f61fd92 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.182", + "version": "1.0.183", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index f23970060f..78f5c8806e 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.182", + "version": "1.0.183", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index b44f1505cd..128413bfed 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.182", + "version": "1.0.183", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index 34a013a51e..4098150aea 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.182", + "version": "1.0.183", "type": "module", "scripts": { "typecheck": "tsgo -b", diff --git a/packages/ui/package.json b/packages/ui/package.json index df5031fb45..863206b13e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.182", + "version": "1.0.183", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index 5d796ef52f..95863c5770 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.182", + "version": "1.0.183", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index d22646849a..cc623c1f5c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.182", + "version": "1.0.183", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 200d6ae8ed..8f1047a1dc 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.182", + "version": "1.0.183", "publisher": "sst-dev", "repository": { "type": "git", From 5072331f0444f0ce67a8266b6687144171582c08 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:35:09 -0600 Subject: [PATCH 005/114] fix(desktop): incorrect state dir on macos --- packages/tauri/src-tauri/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/tauri/src-tauri/src/lib.rs b/packages/tauri/src-tauri/src/lib.rs index ffefbabf6b..3c08841ab8 100644 --- a/packages/tauri/src-tauri/src/lib.rs +++ b/packages/tauri/src-tauri/src/lib.rs @@ -6,7 +6,7 @@ use std::{ sync::{Arc, Mutex}, time::{Duration, Instant}, }; -use tauri::{AppHandle, LogicalSize, Manager, RunEvent, WebviewUrl, WebviewWindow}; +use tauri::{AppHandle, LogicalSize, Manager, RunEvent, WebviewUrl, WebviewWindow, path::BaseDirectory}; use tauri_plugin_clipboard_manager::ClipboardExt; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult}; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; @@ -97,6 +97,11 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { let log_state = app.state::(); let log_state_clone = log_state.inner().clone(); + let state_dir = app + .path() + .resolve("", BaseDirectory::AppLocalData) + .expect("Failed to resolve app local data dir"); + #[cfg(target_os = "windows")] let (mut rx, child) = app .shell() @@ -104,6 +109,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { .unwrap() .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") .env("OPENCODE_CLIENT", "desktop") + .env("XDG_STATE_HOME", &state_dir) .args(["serve", &format!("--port={port}")]) .spawn() .expect("Failed to spawn opencode"); @@ -120,6 +126,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { .command(&shell) .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") .env("OPENCODE_CLIENT", "desktop") + .env("XDG_STATE_HOME", &state_dir) .args([ "-il", "-c", From 36bb02ae4559e2bf372c1125843a81ec8b2d9ed0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 21 Dec 2025 10:36:43 +0000 Subject: [PATCH 006/114] chore: generate --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 7a6f61fd92..d7aec1eb4a 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 78f5c8806e..bb5699a535 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 8865e524cb9831f7c0bf995e1b67159a949ef4de Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Sun, 21 Dec 2025 04:39:46 -0600 Subject: [PATCH 007/114] fix(desktop): allow text selection --- packages/tauri/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tauri/index.html b/packages/tauri/index.html index 8de2d21d01..faeb1a1fde 100644 --- a/packages/tauri/index.html +++ b/packages/tauri/index.html @@ -14,7 +14,7 @@ - + ` + // Inject the script right after the opening tag + const modifiedHtml = html.replace("", `${portScript}`) + return c.html(modifiedHtml) + } + + return response }), ) @@ -2607,14 +2622,9 @@ export namespace Server { idleTimeout: 0, fetch: App().fetch, websocket: websocket, - } as const - if (opts.port === 0) { - try { - return Bun.serve({ ...args, port: 4096 }) - } catch { - // port 4096 not available, fall through to use port 0 - } - } - return Bun.serve({ ...args, port: opts.port }) + }) + // Store the actual port for injection into frontend HTML + serverPort = server.port ?? opts.port + return server } } From dbaac790397bb7e4c3f7def6078b40015e013516 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 06:02:16 -0600 Subject: [PATCH 060/114] fix: server --- packages/opencode/src/server/server.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 69bd5e3be7..3a480228ee 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -53,9 +53,6 @@ globalThis.AI_SDK_LOG_WARNINGS = false export namespace Server { const log = Log.create({ service: "server" }) - // Port that the server is running on, used to inject into frontend HTML - let serverPort: number = 4096 - export const Event = { Connected: BusEvent.define("server.connected", z.object({})), Disposed: BusEvent.define("global.disposed", z.object({})), @@ -2616,15 +2613,24 @@ export namespace Server { return result } + let serverPort: number = 4096 + export function listen(opts: { port: number; hostname: string }) { const args = { hostname: opts.hostname, idleTimeout: 0, fetch: App().fetch, websocket: websocket, - }) - // Store the actual port for injection into frontend HTML - serverPort = server.port ?? opts.port - return server + } as const + if (opts.port === 0) { + try { + serverPort = 4096 + return Bun.serve({ ...args, port: 4096 }) + } catch { + // port 4096 not available, fall through to use port 0 + } + } + serverPort = opts.port + return Bun.serve({ ...args, port: opts.port }) } } From a97631f7693c532ebed0da66a947fb4951e22632 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Dec 2025 12:05:15 +0000 Subject: [PATCH 061/114] ignore: update download stats 2025-12-22 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index eb0f433646..7f59be1aa7 100644 --- a/STATS.md +++ b/STATS.md @@ -177,3 +177,4 @@ | 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) | | 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) | | 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) | +| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) | From 240ad31edd412ca370c5e54dbf4abd5d6eca5c67 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 06:04:56 -0600 Subject: [PATCH 062/114] Revert "fix: server" This reverts commit dbaac790397bb7e4c3f7def6078b40015e013516. --- packages/opencode/src/server/server.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 3a480228ee..69bd5e3be7 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -53,6 +53,9 @@ globalThis.AI_SDK_LOG_WARNINGS = false export namespace Server { const log = Log.create({ service: "server" }) + // Port that the server is running on, used to inject into frontend HTML + let serverPort: number = 4096 + export const Event = { Connected: BusEvent.define("server.connected", z.object({})), Disposed: BusEvent.define("global.disposed", z.object({})), @@ -2613,24 +2616,15 @@ export namespace Server { return result } - let serverPort: number = 4096 - export function listen(opts: { port: number; hostname: string }) { const args = { hostname: opts.hostname, idleTimeout: 0, fetch: App().fetch, websocket: websocket, - } as const - if (opts.port === 0) { - try { - serverPort = 4096 - return Bun.serve({ ...args, port: 4096 }) - } catch { - // port 4096 not available, fall through to use port 0 - } - } - serverPort = opts.port - return Bun.serve({ ...args, port: opts.port }) + }) + // Store the actual port for injection into frontend HTML + serverPort = server.port ?? opts.port + return server } } From 2c16b9fa61b8d8c333c9fb1a1ab51f1089f69079 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 06:05:10 -0600 Subject: [PATCH 063/114] Revert "server: ensure frontend has correct port for PTY websocket connections (#5898)" This reverts commit a05915ddc8f34d592c05ac6a4f4e8d932a9a0964. --- packages/desktop/src/app.tsx | 2 +- packages/opencode/src/server/server.ts | 34 +++++++++----------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/packages/desktop/src/app.tsx b/packages/desktop/src/app.tsx index e5607fd339..2ed529bbc0 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/desktop/src/app.tsx @@ -35,7 +35,7 @@ const url = new URLSearchParams(document.location.search).get("url") || (location.hostname.includes("opencode.ai") || location.hostname.includes("localhost") ? `http://${host}:${port}` - : window.location.origin) + : "/") export function App() { return ( diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 69bd5e3be7..e92c46225d 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -6,6 +6,7 @@ import { describeRoute, generateSpecs, validator, resolver, openAPIRouteHandler import { Hono } from "hono" import { cors } from "hono/cors" import { stream, streamSSE } from "hono/streaming" +import { proxy } from "hono/proxy" import { Session } from "../session" import z from "zod" import { Provider } from "../provider/provider" @@ -53,9 +54,6 @@ globalThis.AI_SDK_LOG_WARNINGS = false export namespace Server { const log = Log.create({ service: "server" }) - // Port that the server is running on, used to inject into frontend HTML - let serverPort: number = 4096 - export const Event = { Connected: BusEvent.define("server.connected", z.object({})), Disposed: BusEvent.define("global.disposed", z.object({})), @@ -2580,25 +2578,12 @@ export namespace Server { }, ) .all("/*", async (c) => { - const response = await fetch(`https://desktop.opencode.ai${c.req.path}`, { - method: c.req.method, + return proxy(`https://desktop.opencode.ai${c.req.path}`, { + ...c.req, headers: { host: "desktop.opencode.ai", }, }) - - const contentType = response.headers.get("content-type") || "" - - // If this is an HTML response, inject the server port - if (contentType.includes("text/html")) { - const html = await response.text() - const portScript = `` - // Inject the script right after the opening tag - const modifiedHtml = html.replace("", `${portScript}`) - return c.html(modifiedHtml) - } - - return response }), ) @@ -2622,9 +2607,14 @@ export namespace Server { idleTimeout: 0, fetch: App().fetch, websocket: websocket, - }) - // Store the actual port for injection into frontend HTML - serverPort = server.port ?? opts.port - return server + } as const + if (opts.port === 0) { + try { + return Bun.serve({ ...args, port: 4096 }) + } catch { + // port 4096 not available, fall through to use port 0 + } + } + return Bun.serve({ ...args, port: opts.port }) } } From c6e9a5c800f7b65f5e7138628443248bed88ecd2 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 22 Dec 2025 12:14:06 +0000 Subject: [PATCH 064/114] release: v1.0.186 --- bun.lock | 30 +++++++++++++------------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++------ packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/tauri/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index aca0296f43..706347b599 100644 --- a/bun.lock +++ b/bun.lock @@ -22,7 +22,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -50,7 +50,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -77,7 +77,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -101,7 +101,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -125,7 +125,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -173,7 +173,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -202,7 +202,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -218,7 +218,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.185", + "version": "1.0.186", "bin": { "opencode": "./bin/opencode", }, @@ -310,7 +310,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -330,7 +330,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.185", + "version": "1.0.186", "devDependencies": { "@hey-api/openapi-ts": "0.88.1", "@tsconfig/node22": "catalog:", @@ -341,7 +341,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -354,7 +354,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@opencode-ai/desktop": "workspace:*", "@solid-primitives/storage": "catalog:", @@ -381,7 +381,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -416,7 +416,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "zod": "catalog:", }, @@ -427,7 +427,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 61eb151f80..2fe98264ad 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 0cf83ecd2f..e1971f3efd 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.185", + "version": "1.0.186", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 1a12d6b063..8c9daf3400 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.185", + "version": "1.0.186", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 2f04a292e2..2ce541a3b4 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.185", + "version": "1.0.186", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ef776b72f8..5f38ac60d6 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.185", + "version": "1.0.186", "description": "", "type": "module", "exports": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 72848ffb9e..602788abbf 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.185", + "version": "1.0.186", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 8475c37880..8d0141d7b6 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.0.185" +version = "1.0.186" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.185/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.185/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.185/opencode-linux-arm64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.185/opencode-linux-x64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.185/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index a8fd285eb2..593499d28a 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.185", + "version": "1.0.186", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b9f8891a00..dfba43513e 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.185", + "version": "1.0.186", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 0b1ba7886f..fd3fd9f511 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 2ad892d9a5..1751488b1f 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index b9259ae621..1d85ced4e9 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index f198e87f57..ff068c65ae 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.185", + "version": "1.0.186", "type": "module", "scripts": { "typecheck": "tsgo -b", diff --git a/packages/ui/package.json b/packages/ui/package.json index ab5a8d575b..f1fd5b9d87 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index cfd59f9742..f2fb03145a 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.185", + "version": "1.0.186", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index e4393f630a..9b2609dece 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.185", + "version": "1.0.186", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 9a3bc2af53..be31398864 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.185", + "version": "1.0.186", "publisher": "sst-dev", "repository": { "type": "git", From 60db171b44193041348bfc00623c7a56e645a0d4 Mon Sep 17 00:00:00 2001 From: Buck Evan Date: Mon, 22 Dec 2025 08:37:43 -0600 Subject: [PATCH 065/114] fix(read): narrow .env file blocking to not block .envrc (#5654) Co-authored-by: Claude --- packages/opencode/src/tool/read.ts | 8 +++-- packages/opencode/test/tool/read.test.ts | 42 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 packages/opencode/test/tool/read.test.ts diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 27426ad241..fd81c4864a 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -60,10 +60,12 @@ export const ReadTool = Tool.define("read", { } const block = iife(() => { - const whitelist = [".env.sample", ".example"] + const basename = path.basename(filepath) + const whitelist = [".env.sample", ".env.example", ".example", ".env.template"] - if (whitelist.some((w) => filepath.endsWith(w))) return false - if (filepath.includes(".env")) return true + if (whitelist.some((w) => basename.endsWith(w))) return false + // Block .env, .env.local, .env.production, etc. but not .envrc + if (/^\.env(\.|$)/.test(basename)) return true return false }) diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts new file mode 100644 index 0000000000..47a7aee2ae --- /dev/null +++ b/packages/opencode/test/tool/read.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from "bun:test" +import path from "path" +import { ReadTool } from "../../src/tool/read" +import { Instance } from "../../src/project/instance" +import { tmpdir } from "../fixture/fixture" + +const ctx = { + sessionID: "test", + messageID: "", + callID: "", + agent: "build", + abort: AbortSignal.any([]), + metadata: () => {}, +} + +describe("tool.read env file blocking", () => { + test.each([ + [".env", true], + [".env.local", true], + [".env.production", true], + [".env.sample", false], + [".env.example", false], + [".envrc", false], + ["environment.ts", false], + ])("%s blocked=%s", async (filename, blocked) => { + await using tmp = await tmpdir({ + init: (dir) => Bun.write(path.join(dir, filename), "content"), + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const read = await ReadTool.init() + const promise = read.execute({ filePath: path.join(tmp.path, filename) }, ctx) + if (blocked) { + await expect(promise).rejects.toThrow("blocked") + } else { + expect((await promise).output).toContain("content") + } + }, + }) + }) +}) From dab2e54df846e6867df188437010f056afb0c1e3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Dec 2025 14:38:20 +0000 Subject: [PATCH 066/114] chore: generate --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index fd3fd9f511..9da158f1db 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 1751488b1f..81498a60f0 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 33c0b125cb2c915a50311246cc1179271763b0b8 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 22 Dec 2025 10:45:51 -0500 Subject: [PATCH 067/114] fix url for web --- packages/desktop/src/app.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/desktop/src/app.tsx b/packages/desktop/src/app.tsx index 2ed529bbc0..9d000b8ff7 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/desktop/src/app.tsx @@ -21,6 +21,7 @@ import Home from "@/pages/home" import DirectoryLayout from "@/pages/directory-layout" import Session from "@/pages/session" import { ErrorPage } from "./pages/error" +import { iife } from "@opencode-ai/util/iife" declare global { interface Window { @@ -28,14 +29,17 @@ declare global { } } -const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" -const port = window.__OPENCODE__?.port ?? import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" +const url = iife(() => { + const param = new URLSearchParams(document.location.search).get("url") + if (param) return param -const url = - new URLSearchParams(document.location.search).get("url") || - (location.hostname.includes("opencode.ai") || location.hostname.includes("localhost") - ? `http://${host}:${port}` - : "/") + if (location.hostname.includes("opencode.ai")) return "http://localhost:4096" + if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}` + if (import.meta.env.VITE_OPENCODE_SERVER) + return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` + + return "/" +}) export function App() { return ( From 8e01f6cc135c7db73f8474c884fbff34b60bdd5c Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:51:17 -0600 Subject: [PATCH 068/114] fix(desktop): diff readability (colors) --- packages/ui/src/pierre/index.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/ui/src/pierre/index.ts b/packages/ui/src/pierre/index.ts index f83fc82a2f..824d96b114 100644 --- a/packages/ui/src/pierre/index.ts +++ b/packages/ui/src/pierre/index.ts @@ -10,6 +10,31 @@ export type DiffProps = FileDiffOptions & { } const unsafeCSS = ` +[data-diffs] { + --diffs-bg: light-dark(var(--diffs-light-bg), var(--diffs-dark-bg)); + --diffs-bg-buffer: var(--diffs-bg-buffer-override, light-dark( color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-mixer)))); + --diffs-bg-hover: var(--diffs-bg-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 97%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-mixer)))); + --diffs-bg-context: var(--diffs-bg-context-override, light-dark( color-mix(in lab, var(--diffs-bg) 98.5%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 92.5%, var(--diffs-mixer)))); + --diffs-bg-separator: var(--diffs-bg-separator-override, light-dark( color-mix(in lab, var(--diffs-bg) 96%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-mixer)))); + --diffs-fg: light-dark(var(--diffs-light), var(--diffs-dark)); + --diffs-fg-number: var(--diffs-fg-number-override, light-dark(color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg)), color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg)))); + --diffs-deletion-base: var(--diffs-deletion-color-override, light-dark(var(--diffs-light-deletion-color, var(--diffs-deletion-color, rgb(255, 0, 0))), var(--diffs-dark-deletion-color, var(--diffs-deletion-color, rgb(255, 0, 0))))); + --diffs-addition-base: var(--diffs-addition-color-override, light-dark(var(--diffs-light-addition-color, var(--diffs-addition-color, rgb(0, 255, 0))), var(--diffs-dark-addition-color, var(--diffs-addition-color, rgb(0, 255, 0))))); + --diffs-modified-base: var(--diffs-modified-color-override, light-dark(var(--diffs-light-modified-color, var(--diffs-modified-color, rgb(0, 0, 255))), var(--diffs-dark-modified-color, var(--diffs-modified-color, rgb(0, 0, 255))))); + --diffs-bg-deletion: var(--diffs-bg-deletion-override, light-dark( color-mix(in lab, var(--diffs-bg) 98%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-number: var(--diffs-bg-deletion-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-hover: var(--diffs-bg-deletion-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 80%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-emphasis: var(--diffs-bg-deletion-emphasis-override, light-dark(rgb(from var(--diffs-deletion-base) r g b / 0.7), rgb(from var(--diffs-deletion-base) r g b / 0.1))); + --diffs-bg-addition: var(--diffs-bg-addition-override, light-dark( color-mix(in lab, var(--diffs-bg) 98%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-addition-base)))); + --diffs-bg-addition-number: var(--diffs-bg-addition-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-addition-base)))); + --diffs-bg-addition-hover: var(--diffs-bg-addition-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 80%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 70%, var(--diffs-addition-base)))); + --diffs-bg-addition-emphasis: var(--diffs-bg-addition-emphasis-override, light-dark(rgb(from var(--diffs-addition-base) r g b / 0.07), rgb(from var(--diffs-addition-base) r g b / 0.1))); + --diffs-selection-base: var(--diffs-modified-base); + --diffs-selection-number-fg: light-dark( color-mix(in lab, var(--diffs-selection-base) 65%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-selection-base) 75%, var(--diffs-mixer))); + --diffs-bg-selection: var(--diffs-bg-selection-override, light-dark( color-mix(in lab, var(--diffs-bg) 82%, var(--diffs-selection-base)), color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-selection-base)))); + --diffs-bg-selection-number: var(--diffs-bg-selection-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-selection-base)), color-mix(in lab, var(--diffs-bg) 60%, var(--diffs-selection-base)))); +} + [data-diffs-header], [data-diffs] { [data-separator-wrapper] { From 753abbe164e2be8a84eb424d56444380f59f6b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lek=C3=AB=20Dobruna?= Date: Mon, 22 Dec 2025 16:56:32 +0100 Subject: [PATCH 069/114] fix: duplicate words in dialog options (#5944) --- .../opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx | 2 +- .../opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index 86317d62a3..ff17b5567e 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -54,7 +54,7 @@ export function DialogMessage(props: { { title: "Copy", value: "message.copy", - description: "copy message text to clipboard", + description: "message text to clipboard", onSelect: async (dialog) => { const msg = message() if (!msg) return diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx index a9446b20d9..c5ef70ef06 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx @@ -11,7 +11,7 @@ export function DialogSubagent(props: { sessionID: string }) { { title: "Open", value: "subagent.view", - description: "open the subagent's session", + description: "the subagent's session", onSelect: (dialog) => { route.navigate({ type: "session", From 29c99ed4abfce2f1fef06dc55be0b575436e028d Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 09:55:38 -0600 Subject: [PATCH 070/114] ci: limit to opencode repo --- .github/workflows/docs-update.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml index bdb3e43b84..aa48c14ad7 100644 --- a/.github/workflows/docs-update.yml +++ b/.github/workflows/docs-update.yml @@ -8,7 +8,8 @@ on: jobs: update-docs: - runs-on: ubuntu-latest + if: github.repository == 'sst/opencode' + runs-on: blacksmith-4vcpu-ubuntu-2404 permissions: id-token: write contents: write @@ -19,6 +20,9 @@ jobs: with: fetch-depth: 0 # Fetch full history to access commits + - name: Setup Bun + uses: ./.github/actions/setup-bun + - name: Get recent commits id: commits run: | From 4a32fa6f0231f581d7f6dfcf3bede4286874df65 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:13:50 -0600 Subject: [PATCH 071/114] fix(share): expanded state and responsiveness --- .../enterprise/src/routes/share/[shareID].tsx | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index a8b2c7f24f..471104d798 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -212,6 +212,7 @@ export default function () { {iife(() => { const [store, setStore] = createStore({ messageId: undefined as string | undefined, + expandedSteps: {} as Record, }) const messages = createMemo(() => data().sessionID @@ -253,20 +254,22 @@ export default function () { const title = () => (
-
-
+
+
v{info().version}
-
- -
{model()?.name ?? modelID()}
-
-
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
+
+ +
{model()?.name ?? modelID()}
+
+
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
{info().title}
@@ -282,6 +285,8 @@ export default function () { setStore("expandedSteps", message.id, (v) => !v)} classes={{ root: "min-w-0 w-full relative", content: @@ -359,6 +364,13 @@ export default function () { { + const id = store.messageId ?? firstUserMessage()!.id! + setStore("expandedSteps", id, (v) => !v) + }} classes={{ root: "grow", content: "flex flex-col justify-between", From 0545c5da2d02c14249c7a6fce04b85e237e6fd00 Mon Sep 17 00:00:00 2001 From: Daniel Polito Date: Mon, 22 Dec 2025 13:59:02 -0300 Subject: [PATCH 072/114] GitHub pull request event (#5335) --- packages/opencode/src/cli/cmd/github.ts | 101 ++++++++++++++++-------- 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index 7234cb12fe..607fc7caf2 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -7,7 +7,7 @@ import { graphql } from "@octokit/graphql" import * as core from "@actions/core" import * as github from "@actions/github" import type { Context } from "@actions/github/lib/context" -import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types" +import type { IssueCommentEvent, PullRequestReviewCommentEvent, PullRequestEvent } from "@octokit/webhooks-types" import { UI } from "../ui" import { cmd } from "./cmd" import { ModelsDev } from "../../provider/models" @@ -127,7 +127,7 @@ type IssueQueryResponse = { const AGENT_USERNAME = "opencode-agent[bot]" const AGENT_REACTION = "eyes" const WORKFLOW_FILE = ".github/workflows/opencode.yml" -const SUPPORTED_EVENTS = ["issue_comment", "pull_request_review_comment", "schedule"] as const +const SUPPORTED_EVENTS = ["issue_comment", "pull_request_review_comment", "schedule", "pull_request"] as const // Parses GitHub remote URLs in various formats: // - https://github.com/owner/repo.git @@ -392,6 +392,7 @@ export const GithubRunCommand = cmd({ core.setFailed(`Unsupported event type: ${context.eventName}`) process.exit(1) } + const isCommentEvent = ["issue_comment", "pull_request_review_comment"].includes(context.eventName) const isScheduleEvent = context.eventName === "schedule" const { providerID, modelID } = normalizeModel() @@ -400,17 +401,17 @@ export const GithubRunCommand = cmd({ const oidcBaseUrl = normalizeOidcBaseUrl() const { owner, repo } = context.repo // For schedule events, payload has no issue/comment data - const payload = isScheduleEvent - ? undefined - : (context.payload as IssueCommentEvent | PullRequestReviewCommentEvent) + const payload = isCommentEvent + ? (context.payload as IssueCommentEvent | PullRequestReviewCommentEvent) + : undefined const issueEvent = payload && isIssueCommentEvent(payload) ? payload : undefined const actor = isScheduleEvent ? undefined : context.actor const issueId = isScheduleEvent ? undefined - : context.eventName === "pull_request_review_comment" - ? (payload as PullRequestReviewCommentEvent).pull_request.number - : (payload as IssueCommentEvent).issue.number + : context.eventName === "issue_comment" + ? (payload as IssueCommentEvent).issue.number + : (payload as PullRequestEvent | PullRequestReviewCommentEvent).pull_request.number const runUrl = `/${owner}/${repo}/actions/runs/${runId}` const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai" @@ -424,11 +425,11 @@ export const GithubRunCommand = cmd({ type PromptFiles = Awaited>["promptFiles"] const triggerCommentId = payload?.comment.id const useGithubToken = normalizeUseGithubToken() - const commentType = isScheduleEvent - ? undefined - : context.eventName === "pull_request_review_comment" + const commentType = isCommentEvent + ? context.eventName === "pull_request_review_comment" ? "pr_review" : "issue" + : undefined try { if (useGithubToken) { @@ -455,7 +456,7 @@ export const GithubRunCommand = cmd({ // Skip permission check for schedule events (no actor to check) if (!isScheduleEvent) { await assertPermissions() - await addReaction(commentType!) + await addReaction(commentType) } // Setup opencode session @@ -494,7 +495,10 @@ export const GithubRunCommand = cmd({ } else { console.log("Response:", response) } - } else if (context.eventName === "pull_request_review_comment" || issueEvent?.issue.pull_request) { + } else if ( + ["pull_request", "pull_request_review_comment"].includes(context.eventName) || + issueEvent?.issue.pull_request + ) { const prData = await fetchPR() // Local PR if (prData.headRepository.nameWithOwner === prData.baseRepository.nameWithOwner) { @@ -509,7 +513,7 @@ export const GithubRunCommand = cmd({ } const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) await createComment(`${response}${footer({ image: !hasShared })}`) - await removeReaction(commentType!) + await removeReaction(commentType) } // Fork PR else { @@ -524,7 +528,7 @@ export const GithubRunCommand = cmd({ } const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) await createComment(`${response}${footer({ image: !hasShared })}`) - await removeReaction(commentType!) + await removeReaction(commentType) } } // Issue @@ -545,10 +549,10 @@ export const GithubRunCommand = cmd({ `${response}\n\nCloses #${issueId}${footer({ image: true })}`, ) await createComment(`Created PR #${pr}${footer({ image: true })}`) - await removeReaction(commentType!) + await removeReaction(commentType) } else { await createComment(`${response}${footer({ image: true })}`) - await removeReaction(commentType!) + await removeReaction(commentType) } } } catch (e: any) { @@ -562,7 +566,7 @@ export const GithubRunCommand = cmd({ } if (!isScheduleEvent) { await createComment(`${msg}${footer()}`) - await removeReaction(commentType!) + await removeReaction(commentType) } core.setFailed(msg) // Also output the clean error message for the action to capture @@ -657,6 +661,9 @@ export const GithubRunCommand = cmd({ .map((m) => m.trim().toLowerCase()) .filter(Boolean) let prompt = (() => { + if (!isCommentEvent) { + return "Review this pull request" + } const body = payload!.comment.body.trim() const bodyLower = body.toLowerCase() if (mentions.some((m) => bodyLower === m)) { @@ -1030,30 +1037,57 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`) } - async function addReaction(commentType: "issue" | "pr_review") { + async function addReaction(commentType?: "issue" | "pr_review") { // Only called for non-schedule events, so triggerCommentId is defined console.log("Adding reaction...") - if (commentType === "pr_review") { - return await octoRest.rest.reactions.createForPullRequestReviewComment({ + if (triggerCommentId) { + if (commentType === "pr_review") { + return await octoRest.rest.reactions.createForPullRequestReviewComment({ + owner, + repo, + comment_id: triggerCommentId!, + content: AGENT_REACTION, + }) + } + return await octoRest.rest.reactions.createForIssueComment({ owner, repo, comment_id: triggerCommentId!, content: AGENT_REACTION, }) } - return await octoRest.rest.reactions.createForIssueComment({ + return await octoRest.rest.reactions.createForIssue({ owner, repo, - comment_id: triggerCommentId!, + issue_number: issueId!, content: AGENT_REACTION, }) } - async function removeReaction(commentType: "issue" | "pr_review") { + async function removeReaction(commentType?: "issue" | "pr_review") { // Only called for non-schedule events, so triggerCommentId is defined console.log("Removing reaction...") - if (commentType === "pr_review") { - const reactions = await octoRest.rest.reactions.listForPullRequestReviewComment({ + if (triggerCommentId) { + if (commentType === "pr_review") { + const reactions = await octoRest.rest.reactions.listForPullRequestReviewComment({ + owner, + repo, + comment_id: triggerCommentId!, + content: AGENT_REACTION, + }) + + const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) + if (!eyesReaction) return + + return await octoRest.rest.reactions.deleteForPullRequestComment({ + owner, + repo, + comment_id: triggerCommentId!, + reaction_id: eyesReaction.id, + }) + } + + const reactions = await octoRest.rest.reactions.listForIssueComment({ owner, repo, comment_id: triggerCommentId!, @@ -1063,29 +1097,28 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) if (!eyesReaction) return - await octoRest.rest.reactions.deleteForPullRequestComment({ + return await octoRest.rest.reactions.deleteForIssueComment({ owner, repo, comment_id: triggerCommentId!, reaction_id: eyesReaction.id, }) - return } - const reactions = await octoRest.rest.reactions.listForIssueComment({ + const reactions = await octoRest.rest.reactions.listForIssue({ owner, repo, - comment_id: triggerCommentId!, + issue_number: issueId!, content: AGENT_REACTION, }) const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) if (!eyesReaction) return - await octoRest.rest.reactions.deleteForIssueComment({ + await octoRest.rest.reactions.deleteForIssue({ owner, repo, - comment_id: triggerCommentId!, + issue_number: issueId!, reaction_id: eyesReaction.id, }) } @@ -1178,7 +1211,7 @@ query($owner: String!, $repo: String!, $number: Int!) { const comments = (issue.comments?.nodes || []) .filter((c) => { const id = parseInt(c.databaseId) - return id !== payload!.comment.id + return id !== triggerCommentId }) .map((c) => ` - ${c.author.login} at ${c.createdAt}: ${c.body}`) @@ -1306,7 +1339,7 @@ query($owner: String!, $repo: String!, $number: Int!) { const comments = (pr.comments?.nodes || []) .filter((c) => { const id = parseInt(c.databaseId) - return id !== payload!.comment.id + return id !== triggerCommentId }) .map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`) From 3f0afd7cf6438c37213d16c178d082a0146bb693 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 11:00:32 -0600 Subject: [PATCH 073/114] ci: tweak docs prompt --- .github/workflows/docs-update.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml index aa48c14ad7..559e74176e 100644 --- a/.github/workflows/docs-update.yml +++ b/.github/workflows/docs-update.yml @@ -66,4 +66,5 @@ jobs: 4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too. Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior. - All doc related commits should start with "docs:" prefix. + Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all. + Try to keep documentation only for large features or changes that already have a good spot to be documented. From af214d35cb24fe20d4f967f07b639c7de39c830c Mon Sep 17 00:00:00 2001 From: Will Marella <162729147+will-marella@users.noreply.github.com> Date: Mon, 22 Dec 2025 12:06:00 -0500 Subject: [PATCH 074/114] Add keybindable commands to navigate between user messages (#5078) Co-authored-by: Will@Cambridge Co-authored-by: Will@Cambridge --- .../src/cli/cmd/tui/routes/session/index.tsx | 62 +++++++++++++++++++ packages/opencode/src/config/config.ts | 2 + packages/sdk/js/src/gen/types.gen.ts | 8 +++ packages/web/src/content/docs/keybinds.mdx | 2 + 4 files changed, 74 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 4ad4dc0ca9..3b1c58966d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -201,6 +201,52 @@ export function Session() { let prompt: PromptRef const keybind = useKeybind() + // Helper: Find next visible message boundary in direction + const findNextVisibleMessage = (direction: "next" | "prev"): string | null => { + const children = scroll.getChildren() + const messagesList = messages() + const scrollTop = scroll.y + + // Get visible messages sorted by position, filtering for valid non-synthetic, non-ignored content + const visibleMessages = children + .filter((c) => { + if (!c.id) return false + const message = messagesList.find((m) => m.id === c.id) + if (!message) return false + + // Check if message has valid non-synthetic, non-ignored text parts + const parts = sync.data.part[message.id] + if (!parts || !Array.isArray(parts)) return false + + return parts.some((part) => part && part.type === "text" && !part.synthetic && !part.ignored) + }) + .sort((a, b) => a.y - b.y) + + if (visibleMessages.length === 0) return null + + if (direction === "next") { + // Find first message below current position + return visibleMessages.find((c) => c.y > scrollTop + 10)?.id ?? null + } + // Find last message above current position + return [...visibleMessages].reverse().find((c) => c.y < scrollTop - 10)?.id ?? null + } + + // Helper: Scroll to message in direction or fallback to page scroll + const scrollToMessage = (direction: "next" | "prev", dialog: ReturnType) => { + const targetID = findNextVisibleMessage(direction) + + if (!targetID) { + scroll.scrollBy(direction === "next" ? scroll.height : -scroll.height) + dialog.clear() + return + } + + const child = scroll.getChildren().find((c) => c.id === targetID) + if (child) scroll.scrollBy(child.y - scroll.y - 1) + dialog.clear() + } + useKeyboard((evt) => { if (dialog.stack.length > 0) return @@ -634,6 +680,22 @@ export function Session() { } }, }, + { + title: "Next message", + value: "session.message.next", + keybind: "messages_next", + category: "Session", + disabled: true, + onSelect: (dialog) => scrollToMessage("next", dialog), + }, + { + title: "Previous message", + value: "session.message.previous", + keybind: "messages_previous", + category: "Session", + disabled: true, + onSelect: (dialog) => scrollToMessage("prev", dialog), + }, { title: "Copy last assistant message", value: "messages.copy", diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 031bdd31ba..daf81f4346 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -456,6 +456,8 @@ export namespace Config { .describe("Scroll messages down by half page"), messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"), messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"), + messages_next: z.string().optional().default("none").describe("Navigate to next message"), + messages_previous: z.string().optional().default("none").describe("Navigate to previous message"), messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"), messages_copy: z.string().optional().default("y").describe("Copy message"), messages_undo: z.string().optional().default("u").describe("Undo message"), diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 964112d812..06993d3f93 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -858,6 +858,14 @@ export type KeybindsConfig = { * Navigate to last message */ messages_last?: string + /** + * Navigate to next message + */ + messages_next?: string + /** + * Navigate to previous message + */ + messages_previous?: string /** * Navigate to last user message */ diff --git a/packages/web/src/content/docs/keybinds.mdx b/packages/web/src/content/docs/keybinds.mdx index 35610af519..b7e370825d 100644 --- a/packages/web/src/content/docs/keybinds.mdx +++ b/packages/web/src/content/docs/keybinds.mdx @@ -32,6 +32,8 @@ OpenCode has a list of keybinds that you can customize through the OpenCode conf "messages_half_page_down": "ctrl+alt+d", "messages_first": "ctrl+g,home", "messages_last": "ctrl+alt+g,end", + "messages_next": "none", + "messages_previous": "none", "messages_copy": "y", "messages_undo": "u", "messages_redo": "r", From 90f232d7f15a8bf09ebeed24297ceb5f4ea4dbad Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Dec 2025 17:06:35 +0000 Subject: [PATCH 075/114] chore: generate --- packages/sdk/js/src/v2/gen/types.gen.ts | 8 ++++++++ packages/sdk/openapi.json | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index f7c0e88a53..e3249848ce 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -890,6 +890,14 @@ export type KeybindsConfig = { * Navigate to last message */ messages_last?: string + /** + * Navigate to next message + */ + messages_next?: string + /** + * Navigate to previous message + */ + messages_previous?: string /** * Navigate to last user message */ diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 12699ee2bd..a1576668a2 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -7308,6 +7308,16 @@ "default": "ctrl+alt+g,end", "type": "string" }, + "messages_next": { + "description": "Navigate to next message", + "default": "none", + "type": "string" + }, + "messages_previous": { + "description": "Navigate to previous message", + "default": "none", + "type": "string" + }, "messages_last_user": { "description": "Navigate to last user message", "default": "none", From 291b65977c0de428f86886acd05f79c46051be90 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 11:27:23 -0600 Subject: [PATCH 076/114] chore(desktop): auto scroll utility --- packages/desktop/src/pages/session.tsx | 94 ++---------- packages/ui/src/components/message-part.tsx | 11 +- .../src/components/session-message-rail.css | 2 +- packages/ui/src/components/session-turn.tsx | 125 ++-------------- packages/ui/src/hooks/create-auto-scroll.tsx | 135 ++++++++++++++++++ packages/ui/src/hooks/index.ts | 1 + 6 files changed, 170 insertions(+), 198 deletions(-) create mode 100644 packages/ui/src/hooks/create-auto-scroll.tsx diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 1f38da3c71..b6f5ccca1d 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -12,7 +12,7 @@ import { createRenderEffect, batch, } from "solid-js" -import { createResizeObserver } from "@solid-primitives/resize-observer" + import { Dynamic } from "solid-js/web" import { useLocal, type LocalFile } from "@/context/local" import { createStore } from "solid-js/store" @@ -27,6 +27,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { Tabs } from "@opencode-ai/ui/tabs" import { useCodeComponent } from "@opencode-ai/ui/context/code" import { SessionTurn } from "@opencode-ai/ui/session-turn" +import { createAutoScroll } from "@opencode-ai/ui/hooks" import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail" import { SessionReview } from "@opencode-ai/ui/session-review" import { @@ -93,13 +94,6 @@ export default function Page() { userInteracted: false, stepsExpanded: true, mobileStepsExpanded: {} as Record, - mobileLastScrollTop: 0, - mobileLastScrollHeight: 0, - mobileAutoScrolled: false, - mobileUserScrolled: false, - mobileContentRef: undefined as HTMLDivElement | undefined, - mobileLastContentWidth: 0, - mobileReflowing: false, messageId: undefined as string | undefined, }) @@ -541,90 +535,20 @@ export default function Page() { const showTabs = createMemo(() => diffs().length > 0 || tabs().all().length > 0) - let mobileScrollRef: HTMLDivElement | undefined const mobileWorking = createMemo(() => status().type !== "idle") - - function handleMobileScroll() { - if (!mobileScrollRef || store.mobileAutoScrolled) return - - const scrollTop = mobileScrollRef.scrollTop - const scrollHeight = mobileScrollRef.scrollHeight - - if (store.mobileReflowing) { - batch(() => { - setStore("mobileLastScrollTop", scrollTop) - setStore("mobileLastScrollHeight", scrollHeight) - }) - return - } - - const scrolledUp = scrollTop < store.mobileLastScrollTop - 50 - if (scrolledUp && mobileWorking()) { - setStore("mobileUserScrolled", true) - setStore("userInteracted", true) - } - - batch(() => { - setStore("mobileLastScrollTop", scrollTop) - setStore("mobileLastScrollHeight", scrollHeight) - }) - } - - function handleMobileInteraction() { - if (mobileWorking()) { - setStore("mobileUserScrolled", true) - setStore("userInteracted", true) - } - } - - function scrollMobileToBottom() { - if (!mobileScrollRef || store.mobileUserScrolled || !mobileWorking()) return - setStore("mobileAutoScrolled", true) - requestAnimationFrame(() => { - mobileScrollRef?.scrollTo({ top: mobileScrollRef.scrollHeight, behavior: "smooth" }) - requestAnimationFrame(() => { - batch(() => { - setStore("mobileLastScrollTop", mobileScrollRef?.scrollTop ?? 0) - setStore("mobileLastScrollHeight", mobileScrollRef?.scrollHeight ?? 0) - setStore("mobileAutoScrolled", false) - }) - }) - }) - } - - createEffect(() => { - if (!mobileWorking()) setStore("mobileUserScrolled", false) + const mobileAutoScroll = createAutoScroll({ + working: mobileWorking, + onUserInteracted: () => setStore("userInteracted", true), }) - createResizeObserver( - () => store.mobileContentRef, - ({ width }) => { - const widthChanged = Math.abs(width - store.mobileLastContentWidth) > 5 - if (widthChanged && store.mobileLastContentWidth > 0) { - setStore("mobileReflowing", true) - requestAnimationFrame(() => { - requestAnimationFrame(() => { - setStore("mobileReflowing", false) - if (mobileWorking() && !store.mobileUserScrolled) { - scrollMobileToBottom() - } - }) - }) - } else if (!store.mobileReflowing) { - scrollMobileToBottom() - } - setStore("mobileLastContentWidth", width) - }, - ) - const MobileTurns = () => (
-
setStore("mobileContentRef", el)} class="flex flex-col gap-45 items-start justify-start mt-4"> +
{(message) => ( tool: string output?: string + status?: string hideDetails?: boolean defaultOpen?: boolean } @@ -398,6 +400,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { tool={part.tool} metadata={metadata} output={part.state.status === "completed" ? part.state.output : undefined} + status={part.state.status} hideDetails={props.hideDetails} defaultOpen={props.defaultOpen} /> @@ -561,6 +564,10 @@ ToolRegistry.register({ const summary = () => (props.metadata.summary ?? []) as { id: string; tool: string; state: { status: string; title?: string } }[] + const autoScroll = createAutoScroll({ + working: () => true, + }) + return ( -
-
+
+
{(item) => { const info = getToolInfo(item.tool) diff --git a/packages/ui/src/components/session-message-rail.css b/packages/ui/src/components/session-message-rail.css index dc2352c220..5729e49204 100644 --- a/packages/ui/src/components/session-message-rail.css +++ b/packages/ui/src/components/session-message-rail.css @@ -17,7 +17,7 @@ display: none; } -@container (min-width: 72rem) { +@container (min-width: 88rem) { [data-slot="session-message-rail-compact"] { display: none; } diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 71e6689f60..e49e70864c 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -3,7 +3,7 @@ import { useData } from "../context" import { useDiffComponent } from "../context/diff" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { checksum } from "@opencode-ai/util/encode" -import { batch, createEffect, createMemo, For, Match, onCleanup, ParentProps, Show, Switch } from "solid-js" +import { createEffect, createMemo, For, Match, onCleanup, ParentProps, Show, Switch } from "solid-js" import { createResizeObserver } from "@solid-primitives/resize-observer" import { DiffChanges } from "./diff-changes" import { Typewriter } from "./typewriter" @@ -19,6 +19,7 @@ import { Button } from "./button" import { Spinner } from "./spinner" import { createStore } from "solid-js/store" import { DateTime, DurationUnit, Interval } from "luxon" +import { createAutoScroll } from "../hooks" function computeStatusFromPart(part: PartType | undefined): string | undefined { if (!part) return undefined @@ -233,17 +234,14 @@ export function SessionTurn( }) } - let scrollRef: HTMLDivElement | undefined + const autoScroll = createAutoScroll({ + working, + onUserInteracted: props.onUserInteracted, + }) + const [store, setStore] = createStore({ - contentRef: undefined as HTMLDivElement | undefined, stickyTitleRef: undefined as HTMLDivElement | undefined, stickyTriggerRef: undefined as HTMLDivElement | undefined, - lastScrollTop: 0, - lastScrollHeight: 0, - lastContainerWidth: 0, - autoScrolled: false, - userScrolled: false, - reflowing: false, stickyHeaderHeight: 0, retrySeconds: 0, status: rawStatus(), @@ -265,104 +263,6 @@ export function SessionTurn( onCleanup(() => clearInterval(timer)) }) - function handleScroll() { - if (!scrollRef || store.autoScrolled) return - - const scrollTop = scrollRef.scrollTop - const scrollHeight = scrollRef.scrollHeight - - if (store.reflowing) { - batch(() => { - setStore("lastScrollTop", scrollTop) - setStore("lastScrollHeight", scrollHeight) - }) - return - } - - const scrollHeightChanged = Math.abs(scrollHeight - store.lastScrollHeight) > 10 - const scrollTopDelta = scrollTop - store.lastScrollTop - - if (scrollHeightChanged && scrollTopDelta < 0) { - const heightRatio = store.lastScrollHeight > 0 ? scrollHeight / store.lastScrollHeight : 1 - const expectedScrollTop = store.lastScrollTop * heightRatio - if (Math.abs(scrollTop - expectedScrollTop) < 100) { - batch(() => { - setStore("lastScrollTop", scrollTop) - setStore("lastScrollHeight", scrollHeight) - }) - return - } - } - - const reset = scrollTop <= 0 && store.lastScrollTop > 0 && working() && !store.userScrolled - if (reset) { - batch(() => { - setStore("lastScrollTop", scrollTop) - setStore("lastScrollHeight", scrollHeight) - }) - requestAnimationFrame(scrollToBottom) - return - } - - const scrolledUp = scrollTop < store.lastScrollTop - 50 && !scrollHeightChanged - if (scrolledUp && working()) { - setStore("userScrolled", true) - props.onUserInteracted?.() - } - - batch(() => { - setStore("lastScrollTop", scrollTop) - setStore("lastScrollHeight", scrollHeight) - }) - } - - function handleInteraction() { - if (working()) { - setStore("userScrolled", true) - props.onUserInteracted?.() - } - } - - function scrollToBottom() { - if (!scrollRef || store.userScrolled || !working()) return - setStore("autoScrolled", true) - requestAnimationFrame(() => { - scrollRef?.scrollTo({ top: scrollRef.scrollHeight, behavior: "smooth" }) - requestAnimationFrame(() => { - batch(() => { - setStore("lastScrollTop", scrollRef?.scrollTop ?? 0) - setStore("lastScrollHeight", scrollRef?.scrollHeight ?? 0) - setStore("autoScrolled", false) - }) - }) - }) - } - - createResizeObserver( - () => store.contentRef, - ({ width }) => { - const widthChanged = Math.abs(width - store.lastContainerWidth) > 5 - if (widthChanged && store.lastContainerWidth > 0) { - setStore("reflowing", true) - requestAnimationFrame(() => { - requestAnimationFrame(() => { - setStore("reflowing", false) - if (working() && !store.userScrolled) { - scrollToBottom() - } - }) - }) - } else if (!store.reflowing) { - scrollToBottom() - } - setStore("lastContainerWidth", width) - }, - ) - - createEffect(() => { - if (!working()) setStore("userScrolled", false) - }) - createResizeObserver( () => store.stickyTitleRef, ({ height }) => { @@ -412,12 +312,17 @@ export function SessionTurn( return (
-
-
+
+
{(msg) => (
setStore("contentRef", el)} + ref={autoScroll.contentRef} data-message={msg().id} data-slot="session-turn-message-container" class={props.classes?.container} diff --git a/packages/ui/src/hooks/create-auto-scroll.tsx b/packages/ui/src/hooks/create-auto-scroll.tsx new file mode 100644 index 0000000000..d201fe36b4 --- /dev/null +++ b/packages/ui/src/hooks/create-auto-scroll.tsx @@ -0,0 +1,135 @@ +import { batch, createEffect } from "solid-js" +import { createStore } from "solid-js/store" +import { createResizeObserver } from "@solid-primitives/resize-observer" + +export interface AutoScrollOptions { + working: () => boolean + onUserInteracted?: () => void +} + +export function createAutoScroll(options: AutoScrollOptions) { + let scrollRef: HTMLElement | undefined + const [store, setStore] = createStore({ + contentRef: undefined as HTMLElement | undefined, + lastScrollTop: 0, + lastScrollHeight: 0, + lastContentWidth: 0, + autoScrolled: false, + userScrolled: false, + reflowing: false, + }) + + function scrollToBottom() { + if (!scrollRef || store.userScrolled || !options.working()) return + setStore("autoScrolled", true) + requestAnimationFrame(() => { + scrollRef?.scrollTo({ top: scrollRef.scrollHeight, behavior: "smooth" }) + requestAnimationFrame(() => { + batch(() => { + setStore("lastScrollTop", scrollRef?.scrollTop ?? 0) + setStore("lastScrollHeight", scrollRef?.scrollHeight ?? 0) + setStore("autoScrolled", false) + }) + }) + }) + } + + function handleScroll() { + if (!scrollRef || store.autoScrolled) return + + const scrollTop = scrollRef.scrollTop + const scrollHeight = scrollRef.scrollHeight + + if (store.reflowing) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + return + } + + const scrollHeightChanged = Math.abs(scrollHeight - store.lastScrollHeight) > 10 + const scrollTopDelta = scrollTop - store.lastScrollTop + + // Handle reflow-caused scroll position changes + if (scrollHeightChanged && scrollTopDelta < 0) { + const heightRatio = store.lastScrollHeight > 0 ? scrollHeight / store.lastScrollHeight : 1 + const expectedScrollTop = store.lastScrollTop * heightRatio + if (Math.abs(scrollTop - expectedScrollTop) < 100) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + return + } + } + + // Handle reset to top while working + const reset = scrollTop <= 0 && store.lastScrollTop > 0 && options.working() && !store.userScrolled + if (reset) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + requestAnimationFrame(scrollToBottom) + return + } + + // Detect intentional scroll up + const scrolledUp = scrollTop < store.lastScrollTop - 50 && !scrollHeightChanged + if (scrolledUp && options.working()) { + setStore("userScrolled", true) + options.onUserInteracted?.() + } + + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + } + + function handleInteraction() { + if (options.working()) { + setStore("userScrolled", true) + options.onUserInteracted?.() + } + } + + // Reset userScrolled when work completes + createEffect(() => { + if (!options.working()) setStore("userScrolled", false) + }) + + // Handle content resize + createResizeObserver( + () => store.contentRef, + ({ width }) => { + const widthChanged = Math.abs(width - store.lastContentWidth) > 5 + if (widthChanged && store.lastContentWidth > 0) { + setStore("reflowing", true) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + setStore("reflowing", false) + if (options.working() && !store.userScrolled) { + scrollToBottom() + } + }) + }) + } else if (!store.reflowing) { + scrollToBottom() + } + setStore("lastContentWidth", width) + }, + ) + + return { + scrollRef: (el: HTMLElement | undefined) => { + scrollRef = el + }, + contentRef: (el: HTMLElement | undefined) => setStore("contentRef", el), + handleScroll, + handleInteraction, + scrollToBottom, + userScrolled: () => store.userScrolled, + } +} diff --git a/packages/ui/src/hooks/index.ts b/packages/ui/src/hooks/index.ts index 7eef78091e..1c90a2e493 100644 --- a/packages/ui/src/hooks/index.ts +++ b/packages/ui/src/hooks/index.ts @@ -1 +1,2 @@ export * from "./use-filtered-list" +export * from "./create-auto-scroll" From 6baee0791f48bcf32eef1e199d0cadca57772b9b Mon Sep 17 00:00:00 2001 From: Daniel Polito Date: Mon, 22 Dec 2025 14:53:58 -0300 Subject: [PATCH 077/114] docs: Github Auto Pull Request Docs (#5974) --- packages/web/src/content/docs/github.mdx | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/web/src/content/docs/github.mdx b/packages/web/src/content/docs/github.mdx index bd792f92fa..25c3ce927a 100644 --- a/packages/web/src/content/docs/github.mdx +++ b/packages/web/src/content/docs/github.mdx @@ -109,6 +109,7 @@ OpenCode can be triggered by the following GitHub events: | `issue_comment` | Comment on an issue or PR | Mention `/opencode` or `/oc` in your comment. OpenCode reads the issue/PR context and can create branches, open PRs, or reply with explanations. | | `pull_request_review_comment` | Comment on specific code lines in a PR | Mention `/opencode` or `/oc` while reviewing code. OpenCode receives file path, line numbers, and diff context for precise responses. | | `schedule` | Cron-based schedule | Run OpenCode on a schedule using the `prompt` input. Useful for automated code reviews, reports, or maintenance tasks. OpenCode can create issues or PRs as needed. | +| `pull_request` | PR opened or updated | Automatically trigger OpenCode when PRs are opened, synchronized, or reopened. Useful for automated reviews without needing to leave a comment. | ### Schedule Example @@ -150,6 +151,43 @@ For scheduled events, the `prompt` input is **required** since there's no commen --- +### Pull Request Example + +Automatically review PRs when they are opened or updated: + +```yaml title=".github/workflows/opencode-review.yml" +name: opencode-review + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + review: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: read + issues: read + steps: + - uses: actions/checkout@v4 + - uses: sst/opencode/github@latest + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + with: + model: anthropic/claude-sonnet-4-20250514 + prompt: | + Review this pull request: + - Check for code quality issues + - Look for potential bugs + - Suggest improvements +``` + +For `pull_request` events, if no `prompt` is provided, OpenCode defaults to reviewing the pull request. + +--- + ## Custom prompts Override the default prompt to customize OpenCode's behavior for your workflow. From 1b1b73b5b317bb190e994506b8e3b57b07a685cb Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 13:09:12 -0600 Subject: [PATCH 078/114] fix(prompt): better summary prompt --- packages/opencode/src/agent/prompt/summary.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/agent/prompt/summary.txt b/packages/opencode/src/agent/prompt/summary.txt index 6c11638db0..c9264db180 100644 --- a/packages/opencode/src/agent/prompt/summary.txt +++ b/packages/opencode/src/agent/prompt/summary.txt @@ -1,4 +1,10 @@ -Summarize the following conversation into 2 sentences MAX explaining what the -assistant did and why -Do not explain the user's input. -Do not speak in the third person about the assistant. +Summarize what was done in this conversation. Write like a pull request description. + +Rules: +- 2-3 sentences max +- Describe the changes made, not the process +- Do not mention running tests, builds, or other validation steps +- Do not explain what the user asked for +- Write in first person (I added..., I fixed...) +- Never ask questions or add new questions +- Only exception: if the conversation ends with an unanswered question to the user, preserve that exact question From 8dfef670b3c3b5e96551fa42cb9099109e57dc19 Mon Sep 17 00:00:00 2001 From: Shpetim <32248437+ShpetimA@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:56:36 +0100 Subject: [PATCH 079/114] [FEATURE]: Show context usage in OpenCode Desktop Context usage (#5979) --- .../desktop/src/components/prompt-input.tsx | 2 + .../src/components/session-context-usage.tsx | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 packages/desktop/src/components/session-context-usage.tsx diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index cba75b212a..94d4ae97e8 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -22,6 +22,7 @@ import { useProviders } from "@/hooks/use-providers" import { useCommand } from "@/context/command" import { persisted } from "@/utils/persist" import { Identifier } from "@/utils/id" +import { SessionContextUsage } from "@/components/session-context-usage" const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"] const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"] @@ -1034,6 +1035,7 @@ export const PromptInput: Component = (props) => { +
(params.id ? (sync.data.message[params.id] ?? []) : [])) + + const cost = createMemo(() => { + const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(total) + }) + + const context = createMemo(() => { + const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage + if (!last) return + const total = + last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write + const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID] + return { + tokens: total.toLocaleString(), + percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null, + } + }) + + return ( + + {(ctx) => ( + +
+ Tokens + {ctx().tokens} +
+
+ Usage + {ctx().percentage ?? 0}% +
+
+ Cost + {cost()} +
+
+ } + placement="top" + > +
+ {`${ctx().percentage ?? 0}%`} + +
+ + )} +
+ ) +} From 750a936ae16c5b385c0031d708d10174955a7a07 Mon Sep 17 00:00:00 2001 From: Tim Kleinschmidt Date: Mon, 22 Dec 2025 21:20:15 +0100 Subject: [PATCH 080/114] support clojure projects with built-in lsp (#5975) --- packages/opencode/src/lsp/server.ts | 21 +++++++++++++++++++++ packages/web/src/content/docs/lsp.mdx | 1 + 2 files changed, 22 insertions(+) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 9390259a8e..e0c8de9988 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -1747,6 +1747,27 @@ export namespace LSPServer { }, } + export const Clojure: Info = { + id: "clojure-lsp", + extensions: [".clj", ".cljs", ".cljc", ".edn"], + root: NearestRoot(["deps.edn", "project.clj", "shadow-cljs.edn", "bb.edn", "build.boot"]), + async spawn(root) { + let bin = Bun.which("clojure-lsp") + if (!bin && process.platform === "win32") { + bin = Bun.which("clojure-lsp.exe") + } + if (!bin) { + log.info("clojure-lsp not found, please install clojure-lsp first") + return + } + return { + process: spawn(bin, ["listen"], { + cwd: root, + }), + } + }, + } + export const Nixd: Info = { id: "nixd", extensions: [".nix"], diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index df97dc3ffa..b546c19915 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -17,6 +17,7 @@ OpenCode comes with several built-in LSP servers for popular languages: | bash | .sh, .bash, .zsh, .ksh | Auto-installs bash-language-server | | clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | Auto-installs for C/C++ projects | | csharp | .cs | `.NET SDK` installed | +| clojure-lsp | .clj, .cljs, .cljc, .edn | `clojure-lsp` command available | | dart | .dart | `dart` command available | | deno | .ts, .tsx, .js, .jsx, .mjs | `deno` command available (auto-detects deno.json/deno.jsonc) | | elixir-ls | .ex, .exs | `elixir` command available | From 7f5e30834f6c541a5abce2f6e7160f2b79c407b7 Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Mon, 22 Dec 2025 21:25:59 +0100 Subject: [PATCH 081/114] upgrade opentui to v0.1.63, enabling kitty alternate keys by default --- bun.lock | 20 ++++++++++---------- packages/opencode/package.json | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bun.lock b/bun.lock index 706347b599..6e222d51a3 100644 --- a/bun.lock +++ b/bun.lock @@ -248,8 +248,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.2", - "@opentui/core": "0.1.62", - "@opentui/solid": "0.1.62", + "@opentui/core": "0.1.63", + "@opentui/solid": "0.1.63", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -1163,21 +1163,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.1.62", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.62", "@opentui/core-darwin-x64": "0.1.62", "@opentui/core-linux-arm64": "0.1.62", "@opentui/core-linux-x64": "0.1.62", "@opentui/core-win32-arm64": "0.1.62", "@opentui/core-win32-x64": "0.1.62", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-T9wsXaS4rFoZF2loaEFqAeuGj5DV3pJzrk18z1um3UfUS2NNH4jyDh5rDdHPb2/YrvO1lU9hd0VoAS/7zUAq/w=="], + "@opentui/core": ["@opentui/core@0.1.63", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.63", "@opentui/core-darwin-x64": "0.1.63", "@opentui/core-linux-arm64": "0.1.63", "@opentui/core-linux-x64": "0.1.63", "@opentui/core-win32-arm64": "0.1.63", "@opentui/core-win32-x64": "0.1.63", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-m4xZQTNCnHXWUWCnGvacJ3Gts1H2aMwP5V/puAG77SDb51jm4W/QOyqAAdgeSakkb9II+8FfUpApX7sfwRXPUg=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.62", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IohPhCkD/DbZEH4M5ft1/o1pI6Vvw2pdxdyoouW/TO1g21W5G8usaWTSRDXO+16BT115Nfb9/DT69H5pzAc2Eg=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.63", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jKCThZGiiublKkP/hMtDtl1MLCw5NU0hMNJdEYvz1WLT9bzliWf6Kb7MIDAmk32XlbQW8/RHdp+hGyGDXK62OQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.62", "", { "os": "darwin", "cpu": "x64" }, "sha512-BqbjQl2sLYrJ1Pq1b3H1I2CFedRiMz0QtZX08IMbyZ5kok+J0A8eQS5tmlbfqoS/VH0de9XiEbuHjG09/nSj1A=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.63", "", { "os": "darwin", "cpu": "x64" }, "sha512-rfNxynHzJpxN9i+SAMnn1NToEc8rYj64BsOxY78JNsm4Gg1Js1uyMaawwh2WbdGknFy4cDXS9QwkUMdMcfnjiw=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.62", "", { "os": "linux", "cpu": "arm64" }, "sha512-P5FleF+W8O4uGubqBvV8DB1AK0+fJhJS8HvfmTZQ2DhSSJJH9Af/WXqitD7ILQY9ltlaUP7l38BC5cVdxnWzCQ=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.63", "", { "os": "linux", "cpu": "arm64" }, "sha512-wG9d6mHWWKZGrzxYS4c+BrcEGXBv/MYBUPSyjP/lD0CxT+X3h6CYhI317JkRyMNfh3vI9CpAKGFTOFvrTTHimQ=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.62", "", { "os": "linux", "cpu": "x64" }, "sha512-l9ab5tgOGcdf8k3NU4TzK/3C8UC0+QuMxgLA/j60BhB1e9bwJleFeYJc+wLIktTUu9QwqCsU4YcuGHL+C2lCzA=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.63", "", { "os": "linux", "cpu": "x64" }, "sha512-TKSzFv4BgWW3RB/iZmq5qxTR4/tRaXo8IZNnVR+LFzShbPOqhUi466AByy9SUmCxD8uYjmMDFYfKtkCy0AnAwA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.62", "", { "os": "win32", "cpu": "arm64" }, "sha512-U1zsOpQl3EGhs8BwoehKAwwVONe+XOXRnXTxMhXw8huF0WWXDWOUL5psjBvfSWPm1rLmagxkQsH84jTSWA/vLA=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.63", "", { "os": "win32", "cpu": "arm64" }, "sha512-CBWPyPognERP0Mq4eC1q01Ado2C2WU+BLTgMdhyt+E2P4w8rPhJ2kCt2MNxO66vQUiynspmZkgjQr0II/VjxWA=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.62", "", { "os": "win32", "cpu": "x64" }, "sha512-JgLZXSaE4q7gUIQb9x6fLWFF3BYlMod2VBhOT1qGBdeveZxsM6ZAno/g+CL9IDUydWfLFadOIBjdYFDVWV2Z2w=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.63", "", { "os": "win32", "cpu": "x64" }, "sha512-qEp6h//FrT+TQiiHm87wZWUwqTPTqIy1ZD+8R+VCUK+usoQiOAD2SqrYnM7W8JkCMGn5/TKm/GaKLyx/qlK4VA=="], - "@opentui/solid": ["@opentui/solid@0.1.62", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.62", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-3th4oZROv3cZvcoL+IwNCEMTKLZaT1BBWKVHxH29wUD0/EPxtowLQCibnjKDqqdTuEUuFA/QtSX52WqQEioR8g=="], + "@opentui/solid": ["@opentui/solid@0.1.63", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.63", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-Gccln4qRucAoaoQEZ4NPAHvGmVYzU/8aKCLG8EPgwCKTcpUzlqYt4357cDHq4cnCNOcXOC06hTz/0pK9r0dqXA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index dfba43513e..325c7f59b3 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -71,8 +71,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.2", - "@opentui/core": "0.1.62", - "@opentui/solid": "0.1.62", + "@opentui/core": "0.1.63", + "@opentui/solid": "0.1.63", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", From eb021a5f92aa56ca5848761dfb0dc2d470a4d41c Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 22 Dec 2025 20:27:29 +0000 Subject: [PATCH 082/114] 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 e1c4419dcb..6beb162c74 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1766125104, - "narHash": "sha256-l/YGrEpLromL4viUo5GmFH3K5M1j0Mb9O+LiaeCPWEM=", + "lastModified": 1766314097, + "narHash": "sha256-laJftWbghBehazn/zxVJ8NdENVgjccsWAdAqKXhErrM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7d853e518814cca2a657b72eeba67ae20ebf7059", + "rev": "306ea70f9eb0fb4e040f8540e2deab32ed7e2055", "type": "github" }, "original": { diff --git a/nix/hashes.json b/nix/hashes.json index 1bc7f95f10..2a7405afc3 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-X9r0BsxLlhhCIioG8xuDVp+mDSlr37ZfqlblvEPrOJQ=" + "nodeModules": "sha256-nu09TYGcdeizJ4aCc5DdS3+uF977LLlMVVEHQPy5Tx8=" } From cd8ecf9722b6ec856135b8000e0cb9053ec0532c Mon Sep 17 00:00:00 2001 From: ja <51257127+anntnzrb@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:31:47 -0500 Subject: [PATCH 083/114] feat(lsp): add Tinymist LSP support for Typst (#5933) --- packages/opencode/src/lsp/language.ts | 2 + packages/opencode/src/lsp/server.ts | 94 +++++++++++++++++++++++++++ packages/web/src/content/docs/lsp.mdx | 1 + 3 files changed, 97 insertions(+) diff --git a/packages/opencode/src/lsp/language.ts b/packages/opencode/src/lsp/language.ts index 12792c7c29..620944a8e0 100644 --- a/packages/opencode/src/lsp/language.ts +++ b/packages/opencode/src/lsp/language.ts @@ -111,4 +111,6 @@ export const LANGUAGE_EXTENSIONS: Record = { ".tfvars": "terraform-vars", ".hcl": "hcl", ".nix": "nix", + ".typ": "typst", + ".typc": "typst", } as const diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index e0c8de9988..b432e5a5d0 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -1798,4 +1798,98 @@ export namespace LSPServer { } }, } + + export const Tinymist: Info = { + id: "tinymist", + extensions: [".typ", ".typc"], + root: NearestRoot(["typst.toml"]), + async spawn(root) { + let bin = Bun.which("tinymist", { + PATH: process.env["PATH"] + path.delimiter + Global.Path.bin, + }) + + if (!bin) { + if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return + log.info("downloading tinymist from GitHub releases") + + const response = await fetch("https://api.github.com/repos/Myriad-Dreamin/tinymist/releases/latest") + if (!response.ok) { + log.error("Failed to fetch tinymist release info") + return + } + + const release = (await response.json()) as { + tag_name?: string + assets?: { name?: string; browser_download_url?: string }[] + } + + const platform = process.platform + const arch = process.arch + + const tinymistArch = arch === "arm64" ? "aarch64" : "x86_64" + let tinymistPlatform: string + let ext: string + + if (platform === "darwin") { + tinymistPlatform = "apple-darwin" + ext = "tar.gz" + } else if (platform === "win32") { + tinymistPlatform = "pc-windows-msvc" + ext = "zip" + } else { + tinymistPlatform = "unknown-linux-gnu" + ext = "tar.gz" + } + + const assetName = `tinymist-${tinymistArch}-${tinymistPlatform}.${ext}` + + const assets = release.assets ?? [] + const asset = assets.find((a) => a.name === assetName) + if (!asset?.browser_download_url) { + log.error(`Could not find asset ${assetName} in tinymist release`) + return + } + + const downloadResponse = await fetch(asset.browser_download_url) + if (!downloadResponse.ok) { + log.error("Failed to download tinymist") + return + } + + const tempPath = path.join(Global.Path.bin, assetName) + await Bun.file(tempPath).write(downloadResponse) + + if (ext === "zip") { + const ok = await Archive.extractZip(tempPath, Global.Path.bin) + .then(() => true) + .catch((error) => { + log.error("Failed to extract tinymist archive", { error }) + return false + }) + if (!ok) return + } else { + await $`tar -xzf ${tempPath} --strip-components=1`.cwd(Global.Path.bin).quiet().nothrow() + } + + await fs.rm(tempPath, { force: true }) + + bin = path.join(Global.Path.bin, "tinymist" + (platform === "win32" ? ".exe" : "")) + + if (!(await Bun.file(bin).exists())) { + log.error("Failed to extract tinymist binary") + return + } + + if (platform !== "win32") { + await $`chmod +x ${bin}`.quiet().nothrow() + } + + log.info("installed tinymist", { bin }) + } + + return { + process: spawn(bin, { cwd: root }), + } + }, + } } diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index b546c19915..230f782d31 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -37,6 +37,7 @@ OpenCode comes with several built-in LSP servers for popular languages: | sourcekit-lsp | .swift, .objc, .objcpp | `swift` installed (`xcode` on macOS) | | svelte | .svelte | Auto-installs for Svelte projects | | terraform | .tf, .tfvars | Auto-installs from GitHub releases | +| tinymist | .typ, .typc | Auto-installs from GitHub releases | | typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project | | vue | .vue | Auto-installs for Vue projects | | yaml-ls | .yaml, .yml | Auto-installs Red Hat yaml-language-server | From 7dc55ac3caf91f763f1305fabc2cc6933b934fb6 Mon Sep 17 00:00:00 2001 From: wienans <40465543+wienans@users.noreply.github.com> Date: Mon, 22 Dec 2025 21:33:45 +0100 Subject: [PATCH 084/114] Add OpenChamber to ecosystem documentation (#5978) --- packages/web/src/content/docs/ecosystem.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/content/docs/ecosystem.mdx b/packages/web/src/content/docs/ecosystem.mdx index 8f7b201b6c..83189e763c 100644 --- a/packages/web/src/content/docs/ecosystem.mdx +++ b/packages/web/src/content/docs/ecosystem.mdx @@ -44,6 +44,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw | [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | Template for building OpenCode plugins | | [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | Neovim frontend for opencode - a terminal-based AI coding agent | | [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK provider for using OpenCode via @opencode-ai/sdk | +| [OpenChamber](https://github.com/btriapitsyn/openchamber)| Web / Desktop App and VS Code Extension for OpenCode| --- From e015bea4627e401d646ebde89ea930befcc54164 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Dec 2025 20:34:21 +0000 Subject: [PATCH 085/114] chore: generate --- packages/web/src/content/docs/ecosystem.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/ecosystem.mdx b/packages/web/src/content/docs/ecosystem.mdx index 83189e763c..0335b27405 100644 --- a/packages/web/src/content/docs/ecosystem.mdx +++ b/packages/web/src/content/docs/ecosystem.mdx @@ -44,7 +44,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw | [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | Template for building OpenCode plugins | | [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | Neovim frontend for opencode - a terminal-based AI coding agent | | [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK provider for using OpenCode via @opencode-ai/sdk | -| [OpenChamber](https://github.com/btriapitsyn/openchamber)| Web / Desktop App and VS Code Extension for OpenCode| +| [OpenChamber](https://github.com/btriapitsyn/openchamber) | Web / Desktop App and VS Code Extension for OpenCode | --- From 25f1643e8e5481b82ee6e770e97877ff86369b95 Mon Sep 17 00:00:00 2001 From: Rohan Godha Date: Mon, 22 Dec 2025 15:50:45 -0500 Subject: [PATCH 086/114] feat(tui): go to parent keybind for subagents (#5762) --- .../src/cli/cmd/tui/routes/session/header.tsx | 3 +++ .../src/cli/cmd/tui/routes/session/index.tsx | 17 +++++++++++++++++ packages/opencode/src/config/config.ts | 1 + packages/sdk/js/src/v2/gen/types.gen.ts | 4 ++++ packages/sdk/openapi.json | 5 +++++ 5 files changed, 30 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx index bfdbfa51b5..098ee83cce 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx @@ -81,6 +81,9 @@ export function Header() { Subagent session + + Parent {keybind.print("session_parent")} + Prev {keybind.print("session_child_cycle_reverse")} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 3b1c58966d..029a012f8e 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -870,6 +870,23 @@ export function Session() { dialog.clear() }, }, + { + title: "Go to parent session", + value: "session.parent", + keybind: "session_parent", + category: "Session", + disabled: true, + onSelect: (dialog) => { + const parentID = session()?.parentID + if (parentID) { + navigate({ + type: "session", + sessionID: parentID, + }) + } + dialog.clear() + }, + }, ]) const revertInfo = createMemo(() => session()?.revert) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index daf81f4346..6520fb3ab9 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -562,6 +562,7 @@ export namespace Config { history_next: z.string().optional().default("down").describe("Next history item"), session_child_cycle: z.string().optional().default("right").describe("Next child session"), session_child_cycle_reverse: z.string().optional().default("left").describe("Previous child session"), + session_parent: z.string().optional().default("up").describe("Go to parent session"), terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"), terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"), }) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index e3249848ce..4eeeceb554 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1122,6 +1122,10 @@ export type KeybindsConfig = { * Previous child session */ session_child_cycle_reverse?: string + /** + * Go to parent session + */ + session_parent?: string /** * Suspend terminal */ diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index a1576668a2..455bd51f8b 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -7598,6 +7598,11 @@ "default": "left", "type": "string" }, + "session_parent": { + "description": "Go to parent session", + "default": "up", + "type": "string" + }, "terminal_suspend": { "description": "Suspend terminal", "default": "ctrl+z", From f9be2bab3af7cee47d6b03d00938570dad6ad541 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 15:12:18 -0600 Subject: [PATCH 087/114] fix: bundle more providers to fix breaking ai sdk issue --- bun.lock | 55 +++++++++++++++++++++- package.json | 9 +++- packages/opencode/package.json | 2 + packages/opencode/src/provider/provider.ts | 18 +++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 6e222d51a3..088f30cda2 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,13 @@ "": { "name": "opencode", "dependencies": { + "@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", "@aws-sdk/client-s3": "3.933.0", "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", @@ -232,10 +239,12 @@ "@ai-sdk/google": "2.0.44", "@ai-sdk/google-vertex": "3.0.81", "@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/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", + "@ai-sdk/xai": "2.0.42", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", @@ -529,22 +538,38 @@ "@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/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], + "@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=="], + + "@ai-sdk/cohere": ["@ai-sdk/cohere@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZjaZFvJlc5XOPi3QwTLEFZbHIgTJc6YGvxz+8zIMGVZi/hdynR8/f/C1A9x6mhzmBtAqi/dZ2h11oouAQH5z4g=="], + + "@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@1.0.30", "", { "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-XK8oRZFApzo6xnS5C+FhWUUkB2itA5Nfon3pU9dJVM0goViq8GwdleZTBRqhu4DE4KJURo5DGWpJr2hfV54cEg=="], + + "@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-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=="], "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="], + "@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw=="], + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "@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/togetherai": ["@ai-sdk/togetherai@1.0.30", "", { "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-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ=="], + + "@ai-sdk/xai": ["@ai-sdk/xai@2.0.42", "", { "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-wlwO4yRoZ/d+ca29vN8SDzxus7POdnL7GBTyRdSrt6icUF0hooLesauC8qRUC4aLxtqvMEc1YHtJOU7ZnLWbTQ=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], @@ -3907,16 +3932,40 @@ "@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/gateway/@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/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=="], + + "@ai-sdk/cerebras/@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/cohere/@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/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/deepinfra/@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/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-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/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/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=="], + "@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=="], + "@ai-sdk/perplexity/@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/togetherai/@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/togetherai/@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/xai/@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/xai/@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=="], + "@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], @@ -4175,6 +4224,8 @@ "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], + "ai/@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=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], diff --git a/package.json b/package.json index 2ddba2c9a4..aceb2015f6 100644 --- a/package.json +++ b/package.json @@ -64,10 +64,17 @@ "turbo": "2.5.6" }, "dependencies": { + "@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", "@aws-sdk/client-s3": "3.933.0", + "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opencode-ai/plugin": "workspace:*", "typescript": "catalog:" }, "repository": { diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 325c7f59b3..79f45a04e1 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -55,10 +55,12 @@ "@ai-sdk/google": "2.0.44", "@ai-sdk/google-vertex": "3.0.81", "@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/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", + "@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/provider/provider.ts b/packages/opencode/src/provider/provider.ts index b8d4dadbd6..5e44643396 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -25,6 +25,15 @@ import { createOpenAI } from "@ai-sdk/openai" import { createOpenAICompatible } from "@ai-sdk/openai-compatible" import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider" import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src" +import { createXai } from "@ai-sdk/xai" +import { createMistral } from "@ai-sdk/mistral" +import { createGroq } from "@ai-sdk/groq" +import { createDeepInfra } from "@ai-sdk/deepinfra" +import { createCerebras } from "@ai-sdk/cerebras" +import { createCohere } from "@ai-sdk/cohere" +import { createGateway } from "@ai-sdk/gateway" +import { createTogetherAI } from "@ai-sdk/togetherai" +import { createPerplexity } from "@ai-sdk/perplexity" export namespace Provider { const log = Log.create({ service: "provider" }) @@ -39,6 +48,15 @@ export namespace Provider { "@ai-sdk/openai": createOpenAI, "@ai-sdk/openai-compatible": createOpenAICompatible, "@openrouter/ai-sdk-provider": createOpenRouter, + "@ai-sdk/xai": createXai, + "@ai-sdk/mistral": createMistral, + "@ai-sdk/groq": createGroq, + "@ai-sdk/deepinfra": createDeepInfra, + "@ai-sdk/cerebras": createCerebras, + "@ai-sdk/cohere": createCohere, + "@ai-sdk/gateway": createGateway, + "@ai-sdk/togetherai": createTogetherAI, + "@ai-sdk/perplexity": createPerplexity, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, } From 855fd07d221d0e2edf65e8ab93d7adf69ef9c1dd Mon Sep 17 00:00:00 2001 From: Github Action Date: Mon, 22 Dec 2025 21:13:50 +0000 Subject: [PATCH 088/114] 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 2a7405afc3..1272821c5e 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-nu09TYGcdeizJ4aCc5DdS3+uF977LLlMVVEHQPy5Tx8=" + "nodeModules": "sha256-SJSVpKmRDS24yzZ3ypYKWVyntOG0TNrrpqQXUkf8gXY=" } From 87b5b34280722c64ebbef8825f6391a6eaa4f388 Mon Sep 17 00:00:00 2001 From: Blake North Date: Mon, 22 Dec 2025 13:20:40 -0800 Subject: [PATCH 089/114] fix(providers.opencode): check config for api key in addition to auth (#5906) --- packages/opencode/src/provider/provider.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 5e44643396..b11ca93680 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -85,6 +85,8 @@ export namespace Provider { const env = Env.all() if (input.env.some((item) => env[item])) return true if (await Auth.get(input.id)) return true + const config = await Config.get() + if (config.provider?.["opencode"]?.options?.apiKey) return true return false })() From 224e5466c1fc5d5289ae05bae4ca8d5a88be347e Mon Sep 17 00:00:00 2001 From: Jon Redeker Date: Mon, 22 Dec 2025 16:21:14 -0500 Subject: [PATCH 090/114] docs: add opencode-morph-fast-apply plugin to ecosystem (#5992) --- packages/web/src/content/docs/ecosystem.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/content/docs/ecosystem.mdx b/packages/web/src/content/docs/ecosystem.mdx index 0335b27405..48ae971fed 100644 --- a/packages/web/src/content/docs/ecosystem.mdx +++ b/packages/web/src/content/docs/ecosystem.mdx @@ -29,6 +29,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw | [opencode-pty](https://github.com/shekohex/opencode-pty.git) | Enables AI agents to run background processes in a PTY, send interactive input to them. | | [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | Track OpenCode usage with Wakatime | | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | Clean up markdown tables produced by LLMs | +| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 10x faster code editing with Morph Fast Apply API and lazy edit markers | | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | Background agents, pre-built LSP/AST/MCP tools, curated agents, Claude Code compatible | | [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | AI-powered automatic Zellij session naming based on OpenCode context | From 64f898601b2aa184b0e75163e7a2fb9542f31bd3 Mon Sep 17 00:00:00 2001 From: Shpetim <32248437+ShpetimA@users.noreply.github.com> Date: Mon, 22 Dec 2025 22:38:54 +0100 Subject: [PATCH 091/114] fix: stop auto execute on sendText vscode extension (#5994) Co-authored-by: Shpetim --- sdks/vscode/src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/vscode/src/extension.ts b/sdks/vscode/src/extension.ts index 63d8d332e4..105ab0293a 100644 --- a/sdks/vscode/src/extension.ts +++ b/sdks/vscode/src/extension.ts @@ -35,7 +35,7 @@ export function activate(context: vscode.ExtensionContext) { if (terminal.name === TERMINAL_NAME) { // @ts-ignore const port = terminal.creationOptions.env?.["_EXTENSION_OPENCODE_PORT"] - port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef) + port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef, false) terminal.show() } }) From 009b0960044257cf2a3213ba49c0a3f64abea202 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 15:40:08 -0600 Subject: [PATCH 092/114] fix: disable claude skill loading for now --- packages/opencode/src/skill/skill.ts | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index 88182c5de4..565d89f257 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -58,7 +58,7 @@ export namespace Skill { ) const SKILL_GLOB = new Bun.Glob("skill/*/SKILL.md") - const CLAUDE_SKILL_GLOB = new Bun.Glob("*/SKILL.md") + // const CLAUDE_SKILL_GLOB = new Bun.Glob("*/SKILL.md") async function discover(): Promise { const directories = await Config.directories() @@ -78,20 +78,20 @@ export namespace Skill { } // Also scan .claude/skills/ walking up from cwd to worktree - for await (const dir of Filesystem.up({ - targets: [".claude/skills"], - start: Instance.directory, - stop: Instance.worktree, - })) { - for await (const match of CLAUDE_SKILL_GLOB.scan({ - cwd: dir, - absolute: true, - onlyFiles: true, - followSymlinks: true, - })) { - paths.push(match) - } - } + // for await (const dir of Filesystem.up({ + // targets: [".claude/skills"], + // start: Instance.directory, + // stop: Instance.worktree, + // })) { + // for await (const match of CLAUDE_SKILL_GLOB.scan({ + // cwd: dir, + // absolute: true, + // onlyFiles: true, + // followSymlinks: true, + // })) { + // paths.push(match) + // } + // } return paths } From 5605fc3f38c2827b2254abe82e2e37bdac8abbe8 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 15:45:31 -0600 Subject: [PATCH 093/114] test: rm claude skills test --- packages/opencode/test/skill/skill.test.ts | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/opencode/test/skill/skill.test.ts b/packages/opencode/test/skill/skill.test.ts index 3d7bc4c236..657ff4ab0b 100644 --- a/packages/opencode/test/skill/skill.test.ts +++ b/packages/opencode/test/skill/skill.test.ts @@ -261,31 +261,31 @@ description: An example skill for testing XML output. }) }) -test("discovers skills from .claude/skills/ directory", async () => { - await using tmp = await tmpdir({ - git: true, - init: async (dir) => { - const skillDir = path.join(dir, ".claude", "skills", "claude-skill") - await Bun.write( - path.join(skillDir, "SKILL.md"), - `--- -name: claude-skill -description: A skill in the .claude/skills directory. ---- +// test("discovers skills from .claude/skills/ directory", async () => { +// await using tmp = await tmpdir({ +// git: true, +// init: async (dir) => { +// const skillDir = path.join(dir, ".claude", "skills", "claude-skill") +// await Bun.write( +// path.join(skillDir, "SKILL.md"), +// `--- +// name: claude-skill +// description: A skill in the .claude/skills directory. +// --- -# Claude Skill -`, - ) - }, - }) +// # Claude Skill +// `, +// ) +// }, +// }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const skills = await Skill.all() - expect(skills.length).toBe(1) - expect(skills[0].name).toBe("claude-skill") - expect(skills[0].location).toContain(".claude/skills/claude-skill/SKILL.md") - }, - }) -}) +// await Instance.provide({ +// directory: tmp.path, +// fn: async () => { +// const skills = await Skill.all() +// expect(skills.length).toBe(1) +// expect(skills[0].name).toBe("claude-skill") +// expect(skills[0].location).toContain(".claude/skills/claude-skill/SKILL.md") +// }, +// }) +// }) From 8c4a816cf600df2f6e687f0eef4408d699a64483 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 22 Dec 2025 15:53:41 -0600 Subject: [PATCH 094/114] ci: add failure case for changelog --- script/publish-start.ts | 80 +++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/script/publish-start.ts b/script/publish-start.ts index 229435ddfa..9213e13524 100755 --- a/script/publish-start.ts +++ b/script/publish-start.ts @@ -19,14 +19,7 @@ if (!Script.preview) { const log = await $`git log v${previous}..HEAD --oneline --format="%h %s" -- packages/opencode packages/sdk packages/plugin packages/tauri packages/desktop`.text() - const commits = log - .split("\n") - .filter((line) => line && !line.match(/^\w+ (ignore:|test:|chore:|ci:)/i)) - .join("\n") - - const opencode = await createOpencode() - const session = await opencode.client.session.create() - console.log("generating changelog since " + previous) + const commits = log.split("\n").filter((line) => line && !line.match(/^\w+ (ignore:|test:|chore:|ci:)/i)) const team = [ "actions-user", @@ -41,20 +34,25 @@ if (!Script.preview) { "opencode-agent[bot]", ] - const raw = await opencode.client.session - .prompt({ - path: { - id: session.data!.id, - }, - body: { - model: { - providerID: "opencode", - modelID: "gemini-3-flash", + async function generateChangelog() { + const opencode = await createOpencode() + const session = await opencode.client.session.create() + console.log("generating changelog since " + previous) + + const raw = await opencode.client.session + .prompt({ + path: { + id: session.data!.id, }, - parts: [ - { - type: "text", - text: ` + body: { + model: { + providerID: "opencode", + modelID: "gemini-3-flash", + }, + parts: [ + { + type: "text", + text: ` Analyze these commits and generate a changelog of all notable user facing changes, grouped by area. Each commit below includes: @@ -62,7 +60,7 @@ if (!Script.preview) { - [areas: ...] showing which areas of the codebase were modified Commits between ${previous} and HEAD: - ${commits} + ${commits.join("\n")} Group the changes into these categories based on the [areas: ...] tags (omit any category with no changes): - **TUI**: Changes to "opencode" area (the terminal/CLI interface) @@ -105,20 +103,34 @@ if (!Script.preview) { - Added OIDC_BASE_URL support for custom GitHub App installations (@elithrar) `, - }, - ], - }, - }) - .then((x) => x.data?.parts?.find((y) => y.type === "text")?.text) - for (const line of raw?.split("\n") ?? []) { - if (line.startsWith("- ")) { - notes.push(line) + }, + ], + }, + }) + .then((x) => x.data?.parts?.find((y) => y.type === "text")?.text) + opencode.server.close() + return raw + } + + const timeout = new Promise((resolve) => setTimeout(() => resolve(null), 120_000)) + const raw = await Promise.race([generateChangelog(), timeout]) + + if (raw) { + for (const line of raw.split("\n")) { + if (line.startsWith("- ")) { + notes.push(line) + } + } + console.log("---- Generated Changelog ----") + console.log(notes.join("\n")) + console.log("-----------------------------") + } else { + console.log("Changelog generation timed out, using raw commits") + for (const commit of commits) { + const message = commit.replace(/^\w+ /, "") + notes.push(`- ${message}`) } } - console.log("---- Generated Changelog ----") - console.log(notes.join("\n")) - console.log("-----------------------------") - opencode.server.close() const compare = await $`gh api "/repos/sst/opencode/compare/v${previous}...HEAD" --jq '.commits[] | {login: .author.login, message: .commit.message}'`.text() From e4d8a117c4c4990a3c64a0ebf6699dcc743da644 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 22 Dec 2025 21:58:41 +0000 Subject: [PATCH 095/114] release: v1.0.187 --- bun.lock | 30 +++++++++++++------------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++------ packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/tauri/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index 088f30cda2..d0f1f1bb8b 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -57,7 +57,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -84,7 +84,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -108,7 +108,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -132,7 +132,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -180,7 +180,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -209,7 +209,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -225,7 +225,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.186", + "version": "1.0.187", "bin": { "opencode": "./bin/opencode", }, @@ -319,7 +319,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -339,7 +339,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.186", + "version": "1.0.187", "devDependencies": { "@hey-api/openapi-ts": "0.88.1", "@tsconfig/node22": "catalog:", @@ -350,7 +350,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -363,7 +363,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@opencode-ai/desktop": "workspace:*", "@solid-primitives/storage": "catalog:", @@ -390,7 +390,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -425,7 +425,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "zod": "catalog:", }, @@ -436,7 +436,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 2fe98264ad..41f707959d 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.0.186", + "version": "1.0.187", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index e1971f3efd..fadda682f5 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.186", + "version": "1.0.187", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 8c9daf3400..09dedec271 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.186", + "version": "1.0.187", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 2ce541a3b4..2ef79b1db3 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.186", + "version": "1.0.187", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 5f38ac60d6..87f28ed1b5 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.186", + "version": "1.0.187", "description": "", "type": "module", "exports": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 602788abbf..c0d35bba14 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.186", + "version": "1.0.187", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 8d0141d7b6..ae25b8d9eb 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.0.186" +version = "1.0.187" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-linux-arm64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-linux-x64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.186/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.187/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 593499d28a..19a772d618 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.186", + "version": "1.0.187", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 79f45a04e1..d9b500da18 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.186", + "version": "1.0.187", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 9da158f1db..3dccb8f0fb 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.186", + "version": "1.0.187", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 81498a60f0..50457f02cc 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.186", + "version": "1.0.187", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index 1d85ced4e9..3aa1564d0a 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.186", + "version": "1.0.187", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index ff068c65ae..7b46a4f1aa 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.186", + "version": "1.0.187", "type": "module", "scripts": { "typecheck": "tsgo -b", diff --git a/packages/ui/package.json b/packages/ui/package.json index f1fd5b9d87..6e94553926 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.186", + "version": "1.0.187", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index f2fb03145a..56b0b59372 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.186", + "version": "1.0.187", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 9b2609dece..304a259932 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.186", + "version": "1.0.187", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index be31398864..5287bcfd75 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.186", + "version": "1.0.187", "publisher": "sst-dev", "repository": { "type": "git", From 740fcd243cb0bb4bd5a9c6aa413cdfb46ed7e2fb Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 22 Dec 2025 17:00:21 -0500 Subject: [PATCH 096/114] ignore: update GitHub stars to 41K and project stats to reflect current growth --- packages/console/app/src/config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/console/app/src/config.ts b/packages/console/app/src/config.ts index 29df86cbd8..bf20681ae1 100644 --- a/packages/console/app/src/config.ts +++ b/packages/console/app/src/config.ts @@ -9,8 +9,8 @@ export const config = { github: { repoUrl: "https://github.com/sst/opencode", starsFormatted: { - compact: "38K", - full: "38,000", + compact: "41K", + full: "41,000", }, }, @@ -22,8 +22,8 @@ export const config = { // Static stats (used on landing page) stats: { - contributors: "400", - commits: "5,000", + contributors: "450", + commits: "6,000", monthlyUsers: "400,000", }, } as const From 6011200128aa04aa0707bae30433a402a1780d53 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Dec 2025 22:01:27 +0000 Subject: [PATCH 097/114] chore: generate --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 3dccb8f0fb..29c56d0be3 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 50457f02cc..0ca37268bf 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -29,4 +29,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 526c723e62eed701c8ff89829a9ca40418caa918 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 22 Dec 2025 17:11:02 -0500 Subject: [PATCH 098/114] support glm 4.7 --- packages/opencode/src/provider/transform.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 76403a4ed5..d86fe90222 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -216,6 +216,7 @@ export namespace ProviderTransform { if (id.includes("claude")) return undefined if (id.includes("gemini-3-pro")) return 1.0 if (id.includes("glm-4.6")) return 1.0 + if (id.includes("glm-4.7")) return 1.0 if (id.includes("minimax-m2")) return 1.0 if (id.includes("kimi-k2")) { if (id.includes("thinking")) return 1.0 From 1aae1c795db6b57b1404c40ee8a9d894bc9fd745 Mon Sep 17 00:00:00 2001 From: Viktor Nagy <126671+nagyv@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:29:28 +0100 Subject: [PATCH 099/114] Add gitlab-opencode to GitLab docs The current GitLab page describes OpenCode integration through GitLab Duo. GitLab Duo is a paying functionality and is limited to workflows supported by GitLab. GitLab-OpenCode is a community project that offers more flexiblity, better customization and easier setup to use OpenCode in GitLab. On the downside, it does not have the level of integration into GitLab as Duo does. --- packages/web/src/content/docs/gitlab.mdx | 46 ++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/web/src/content/docs/gitlab.mdx b/packages/web/src/content/docs/gitlab.mdx index 66c006c803..3810ba3185 100644 --- a/packages/web/src/content/docs/gitlab.mdx +++ b/packages/web/src/content/docs/gitlab.mdx @@ -3,12 +3,52 @@ title: GitLab description: Use OpenCode in GitLab issues and merge requests. --- +## Integration options + +There are at least two approaches to run OpenCode in GitLab: + +- Run it in GitLab pipelines as a regular pipeline +- Run it through GitLab Duo + +In both cases, OpenCode will run on your GitLab runners. + +## GitLab CI integration + +OpenCode works in a regular GitLab pipeline. You build it into a pipeline as a [CI component](https://docs.gitlab.com/ee/ci/components/) + +--- + +### Features + +- **Use custom configuration per job**: Configure OpenCode with a [custom configuration directory](./config/#custom-directory) to enable/disable functionality per OpenCode invocation. +- **Minimal setup**: The CI component sets up OpenCode in the background, you only need to create the OpenCode configuration and the initial prompt. +- **Flexible**: The CI component supports several inputs for customizing its behavior + +### Setup + +1. Store your OpenCode authentication JSON as a File type CI environment variables under **Settings -> CI/CD -> Variables**. Tip: Mark it "Masked and hidden". +2. Add code blocks like the following to your `.gitlab-ci.yml` file: + +``` +include: + - component: $CI_SERVER_FQDN/nagyv/gitlab-opencode/opencode@1.0.0 + inputs: + config_dir: ${CI_PROJECT_DIR}/opencode-config + auth_json: $OPENCODE_AUTH_JSON # The variable name for your OpenCode authentication JSON + command: optional-custom-command + message: "Your prompt here" +``` + +See more inputs and use cases in [its documentation](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode). + +## GitLab Duo integration + OpenCode integrates with your GitLab workflow. Mention `@opencode` in a comment, and OpenCode will execute tasks within your GitLab CI pipeline. --- -## Features +### Features - **Triage issues**: Ask OpenCode to look into an issue and explain it to you. - **Fix and implement**: Ask OpenCode to fix an issue or implement a feature. @@ -17,7 +57,7 @@ Mention `@opencode` in a comment, and OpenCode will execute tasks within your Gi --- -## Setup +### Setup OpenCode runs in your GitLab CI/CD pipeline, here's what you'll need to set it up: @@ -113,7 +153,7 @@ You can refer to the [GitLab CLI agents docs](https://docs.gitlab.com/user/duo_a --- -## Examples +### Examples Here are some examples of how you can use OpenCode in GitLab. From 9b6c9f64f7cce2be89b9f4826e348da1edeb64a4 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:19:57 -0600 Subject: [PATCH 100/114] feat(desktop): review pane toggle --- packages/desktop/src/components/header.tsx | 29 ++++++++++++++++++++++ packages/desktop/src/context/layout.tsx | 15 +++++++++++ packages/desktop/src/pages/session.tsx | 11 +++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/desktop/src/components/header.tsx b/packages/desktop/src/components/header.tsx index c5ecd9871c..ec7cdfa25b 100644 --- a/packages/desktop/src/components/header.tsx +++ b/packages/desktop/src/components/header.tsx @@ -109,6 +109,35 @@ export function Header(props: {
+
+ } + > + +