diff --git a/bun.lock b/bun.lock index 58f41a67a64..2f8cd602322 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -46,7 +46,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -73,7 +73,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -97,7 +97,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -121,7 +121,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -162,7 +162,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -188,7 +188,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -204,7 +204,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.106", + "version": "1.0.107", "bin": { "opencode": "./bin/opencode", }, @@ -290,7 +290,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -310,7 +310,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.106", + "version": "1.0.107", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -321,7 +321,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -334,7 +334,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", @@ -347,7 +347,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -379,7 +379,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "zod": "catalog:", }, @@ -389,7 +389,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.106", + "version": "1.0.107", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/flake.lock b/flake.lock index 1150e275150..826bf4d8608 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1763618868, - "narHash": "sha256-v5afmLjn/uyD9EQuPBn7nZuaZVV9r+JerayK/4wvdWA=", + "lastModified": 1763806073, + "narHash": "sha256-FHsEKDvfWpzdADWj99z7vBk4D716Ujdyveo5+A048aI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a8d610af3f1a5fb71e23e08434d8d61a466fc942", + "rev": "878e468e02bfabeda08c79250f7ad583037f2227", "type": "github" }, "original": { diff --git a/nix/bundle.ts b/nix/bundle.ts new file mode 100644 index 00000000000..effb1dff7cc --- /dev/null +++ b/nix/bundle.ts @@ -0,0 +1,40 @@ +#!/usr/bin/env bun + +import solidPlugin from "./node_modules/@opentui/solid/scripts/solid-plugin" +import path from "path" +import fs from "fs" + +const dir = process.cwd() +const parser = fs.realpathSync(path.join(dir, "node_modules/@opentui/core/parser.worker.js")) +const worker = "./src/cli/cmd/tui/worker.ts" +const version = process.env.OPENCODE_VERSION ?? "local" +const channel = process.env.OPENCODE_CHANNEL ?? "local" + +fs.rmSync(path.join(dir, "dist"), { recursive: true, force: true }) + +const result = await Bun.build({ + entrypoints: ["./src/index.ts", worker, parser], + outdir: "./dist", + target: "bun", + sourcemap: "none", + tsconfig: "./tsconfig.json", + plugins: [solidPlugin], + external: ["@opentui/core"], + define: { + OPENCODE_VERSION: `'${version}'`, + OPENCODE_CHANNEL: `'${channel}'`, + // Leave undefined so runtime picks bundled/dist worker or fallback in code. + OPENCODE_WORKER_PATH: "undefined", + OTUI_TREE_SITTER_WORKER_PATH: 'new URL("./cli/cmd/tui/parser.worker.js", import.meta.url).href', + }, +}) + +if (!result.success) { + console.error("bundle failed") + for (const log of result.logs) console.error(log) + process.exit(1) +} + +const parserOut = path.join(dir, "dist/src/cli/cmd/tui/parser.worker.js") +fs.mkdirSync(path.dirname(parserOut), { recursive: true }) +await Bun.write(parserOut, Bun.file(parser)) diff --git a/nix/hashes.json b/nix/hashes.json index 6063a49bd3f..16424b3a035 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-/ZkyVHgRMjhzBpnDNTR6X+TomtTMarVU7gmq9Z8Czr8=" + "nodeModules": "sha256-m7hL9Uzqk+oa2/FtgkzEPgi+m/VZP1SvjpgYHNjiS1c=" } diff --git a/nix/opencode.nix b/nix/opencode.nix index bec2997608e..ff536cf8ff8 100644 --- a/nix/opencode.nix +++ b/nix/opencode.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }: +{ lib, stdenvNoCC, bun, fzf, ripgrep, makeBinaryWrapper }: args: let scripts = args.scripts; @@ -28,64 +28,89 @@ stdenvNoCC.mkDerivation (finalAttrs: { makeBinaryWrapper ]; - configurePhase = '' - runHook preConfigure - cp -R ${finalAttrs.node_modules}/. . - runHook postConfigure - ''; - env.MODELS_DEV_API_JSON = args.modelsDev; env.OPENCODE_VERSION = args.version; env.OPENCODE_CHANNEL = "stable"; + dontConfigure = true; buildPhase = '' runHook preBuild - cp ${scripts + "/bun-build.ts"} bun-build.ts + cp -r ${finalAttrs.node_modules}/node_modules . + cp -r ${finalAttrs.node_modules}/packages . - substituteInPlace bun-build.ts \ - --replace '@VERSION@' "${finalAttrs.version}" + ( + cd packages/opencode - export BUN_COMPILE_TARGET=${args.target} - bun --bun bun-build.ts + chmod -R u+w ./node_modules + mkdir -p ./node_modules/@opencode-ai + rm -f ./node_modules/@opencode-ai/{script,sdk,plugin} + ln -s $(pwd)/../../packages/script ./node_modules/@opencode-ai/script + ln -s $(pwd)/../../packages/sdk/js ./node_modules/@opencode-ai/sdk + ln -s $(pwd)/../../packages/plugin ./node_modules/@opencode-ai/plugin + + cp ${./bundle.ts} ./bundle.ts + chmod +x ./bundle.ts + bun run ./bundle.ts + ) runHook postBuild ''; - dontStrip = true; - installPhase = '' runHook preInstall cd packages/opencode - if [ ! -f opencode ]; then - echo "ERROR: opencode binary not found in $(pwd)" - ls -la + if [ ! -d dist ]; then + echo "ERROR: dist directory missing after bundle step" exit 1 fi - if [ ! -f opencode-worker.js ]; then - echo "ERROR: opencode worker bundle not found in $(pwd)" - ls -la + + mkdir -p $out/lib/opencode + cp -r dist $out/lib/opencode/ + chmod -R u+w $out/lib/opencode/dist + + # Select bundled worker assets deterministically (sorted find output) + worker_file=$(find "$out/lib/opencode/dist" -type f \( -path '*/tui/worker.*' -o -name 'worker.*' \) | sort | head -n1) + parser_worker_file=$(find "$out/lib/opencode/dist" -type f -name 'parser.worker.*' | sort | head -n1) + if [ -z "$worker_file" ]; then + echo "ERROR: bundled worker not found" exit 1 fi - install -Dm755 opencode $out/bin/opencode - install -Dm644 opencode-worker.js $out/bin/opencode-worker.js - if [ -f opencode-assets.manifest ]; then - while IFS= read -r asset; do - [ -z "$asset" ] && continue - if [ ! -f "$asset" ]; then - echo "ERROR: referenced asset \"$asset\" missing" - exit 1 - fi - install -Dm644 "$asset" "$out/bin/$(basename "$asset")" - done < opencode-assets.manifest - fi + main_wasm=$(printf '%s\n' "$out"/lib/opencode/dist/tree-sitter-*.wasm | sort | head -n1) + wasm_list=$(find "$out/lib/opencode/dist" -maxdepth 1 -name 'tree-sitter-*.wasm' -print) + for patch_file in "$worker_file" "$parser_worker_file"; do + [ -z "$patch_file" ] && continue + [ ! -f "$patch_file" ] && continue + if [ -n "$wasm_list" ] && grep -q 'tree-sitter' "$patch_file"; then + # Rewrite wasm references to absolute store paths to avoid runtime resolve failures. + bun --bun ${scripts + "/patch-wasm.ts"} "$patch_file" "$main_wasm" $wasm_list + fi + done + + mkdir -p $out/lib/opencode/node_modules + cp -r ../../node_modules/.bun $out/lib/opencode/node_modules/ + mkdir -p $out/lib/opencode/node_modules/@opentui + + mkdir -p $out/bin + makeWrapper ${bun}/bin/bun $out/bin/opencode \ + --add-flags "run" \ + --add-flags "$out/lib/opencode/dist/src/index.js" \ + --prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]} \ + --argv0 opencode + runHook postInstall ''; - postFixup = '' - wrapProgram "$out/bin/opencode" --prefix PATH : ${lib.makeBinPath [ fzf ripgrep ]} + postInstall = '' + for pkg in $out/lib/opencode/node_modules/.bun/@opentui+core-* $out/lib/opencode/node_modules/.bun/@opentui+solid-* $out/lib/opencode/node_modules/.bun/@opentui+core@* $out/lib/opencode/node_modules/.bun/@opentui+solid@*; do + if [ -d "$pkg" ]; then + pkgName=$(basename "$pkg" | sed 's/@opentui+\([^@]*\)@.*/\1/') + ln -sf ../.bun/$(basename "$pkg")/node_modules/@opentui/$pkgName \ + $out/lib/opencode/node_modules/@opentui/$pkgName + fi + done ''; meta = { diff --git a/nix/scripts/canonicalize-node-modules.ts b/nix/scripts/canonicalize-node-modules.ts index bb004f3c54c..faa6f63402e 100644 --- a/nix/scripts/canonicalize-node-modules.ts +++ b/nix/scripts/canonicalize-node-modules.ts @@ -24,15 +24,13 @@ for (const entry of directories) { if (!info.isDirectory()) { continue } - const marker = entry.lastIndexOf("@") - if (marker <= 0) { + const parsed = parseEntry(entry) + if (!parsed) { continue } - const slug = entry.slice(0, marker).replace(/\+/g, "/") - const version = entry.slice(marker + 1) - const list = versions.get(slug) ?? [] - list.push({ dir: full, version, label: entry }) - versions.set(slug, list) + const list = versions.get(parsed.name) ?? [] + list.push({ dir: full, version: parsed.version, label: entry }) + versions.set(parsed.name, list) } const semverModule = (await import(join(bunRoot, "node_modules/semver"))) as @@ -79,6 +77,12 @@ for (const [slug, entry] of Array.from(selections.entries()).sort((a, b) => a[0] await mkdir(parent, { recursive: true }) const linkPath = join(parent, leaf) const desired = join(entry.dir, "node_modules", slug) + const exists = await lstat(desired) + .then((info) => info.isDirectory()) + .catch(() => false) + if (!exists) { + continue + } const relativeTarget = relative(parent, desired) const resolved = relativeTarget.length === 0 ? "." : relativeTarget await rm(linkPath, { recursive: true, force: true }) @@ -94,3 +98,16 @@ for (const line of rewrites.slice(0, 20)) { if (rewrites.length > 20) { console.log(" ...") } + +function parseEntry(label: string) { + const marker = label.startsWith("@") ? label.indexOf("@", 1) : label.indexOf("@") + if (marker <= 0) { + return null + } + const name = label.slice(0, marker).replace(/\+/g, "/") + const version = label.slice(marker + 1) + if (!name || !version) { + return null + } + return { name, version } +} diff --git a/nix/scripts/patch-wasm.ts b/nix/scripts/patch-wasm.ts new file mode 100644 index 00000000000..99f8a40e9f3 --- /dev/null +++ b/nix/scripts/patch-wasm.ts @@ -0,0 +1,39 @@ +#!/usr/bin/env bun + +import fs from "fs" +import path from "path" + +/** + * Rewrite tree-sitter wasm references inside a JS file to absolute paths. + * argv: [node, script, file, mainWasm, ...wasmPaths] + */ +const [, , file, mainWasm, ...wasmPaths] = process.argv + +if (!file || !mainWasm) { + console.error("usage: patch-wasm [wasmPaths...]") + process.exit(1) +} + +const content = fs.readFileSync(file, "utf8") +const byName = new Map() + +for (const wasm of wasmPaths) { + const name = path.basename(wasm) + byName.set(name, wasm) +} + +let next = content + +for (const [name, wasmPath] of byName) { + next = next.replaceAll(name, wasmPath) +} + +next = next.replaceAll("tree-sitter.wasm", mainWasm).replaceAll("web-tree-sitter/tree-sitter.wasm", mainWasm) + +// Collapse any relative prefixes before absolute store paths (e.g., "../../../..//nix/store/...") +next = next.replace(/(\.\/)+/g, "./") +next = next.replace(/(\.\.\/)+\/?(\/nix\/store[^"']+)/g, "/$2") +next = next.replace(/(["'])\/{2,}(\/nix\/store[^"']+)(["'])/g, "$1/$2$3") +next = next.replace(/(["'])\/\/(nix\/store[^"']+)(["'])/g, "$1/$2$3") + +if (next !== content) fs.writeFileSync(file, next) diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 322afb56fb3..1669fb50a61 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.106", + "version": "1.0.107", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 3aa4b52d50e..db0904e3ba5 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.106", + "version": "1.0.107", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 6b6cccd5a46..34e7944fbec 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.106", + "version": "1.0.107", "$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 42a1647feca..8bcde9ee4d3 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.106", + "version": "1.0.107", "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 c3d8d8070a0..aef78d8d476 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.106", + "version": "1.0.107", "description": "", "type": "module", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 4bdcd970047..37970c2ee89 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.106", + "version": "1.0.107", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 771be59333a..f1c6a36e7e3 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The AI coding agent built for the terminal" -version = "1.0.106" +version = "1.0.107" 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.106/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.107/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.106/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.107/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.106/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.107/opencode-linux-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.106/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.107/opencode-linux-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.106/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.107/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index e2abb624639..9ee6a260812 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.106", + "version": "1.0.107", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d275033a2c1..25ee3983bfb 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.106", + "version": "1.0.107", "name": "opencode", "type": "module", "private": true, diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index f9963fae880..c3b38aab2a2 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -94,6 +94,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const agent = agents().find((x) => x.name === name) if (agent?.color) return RGBA.fromHex(agent.color) const index = agents().findIndex((x) => x.name === name) + if (index === -1) return colors()[0] return colors()[index % colors().length] }, } diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx index 4e3cc35315e..88b9616b06a 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx @@ -210,7 +210,7 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ const kv = useKV() const [store, setStore] = createStore({ themes: DEFAULT_THEMES, - mode: props.mode, + mode: kv.get("theme_mode", props.mode), active: (sync.data.config.theme ?? kv.get("theme", "opencode")) as string, ready: false, }) @@ -262,6 +262,7 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ }, setMode(mode: "dark" | "light") { setStore("mode", mode) + kv.set("theme_mode", mode) }, set(theme: string) { setStore("active", theme) 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 547b9a7d2a1..0169c68e617 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1008,7 +1008,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las if (!final()) return 0 if (!props.message.time.completed) return 0 const user = messages().find((x) => x.role === "user" && x.id === props.message.parentID) - if (!user) return 0 + if (!user || !user.time) return 0 return props.message.time.completed - user.time.created }) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 9ba799f09e4..8302bfefe95 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -18,6 +18,9 @@ export function Sidebar(props: { sessionID: string }) { const [todoExpanded, setTodoExpanded] = createSignal(true) const [lspExpanded, setLspExpanded] = createSignal(true) + // Sort MCP servers alphabetically for consistent display order + const mcpEntries = createMemo(() => Object.entries(sync.data.mcp).sort(([a], [b]) => a.localeCompare(b))) + const cost = createMemo(() => { const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) return new Intl.NumberFormat("en-US", { @@ -58,22 +61,22 @@ export function Sidebar(props: { sessionID: string }) { {context()?.percentage ?? 0}% used {cost()} spent - 0}> + 0}> Object.keys(sync.data.mcp).length > 2 && setMcpExpanded(!mcpExpanded())} + onMouseDown={() => mcpEntries().length > 2 && setMcpExpanded(!mcpExpanded())} > - 2}> + 2}> {mcpExpanded() ? "▼" : "▶"} MCP - - + + {([key, item]) => ( { + const workerPath = await iife(async () => { if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH - if (hasBundledWorker) return bundledWorker - return defaultWorker - })() + if (await Bun.file(distWorker).exists()) return distWorker + return localWorker + }) try { process.chdir(cwd) } catch (e) { diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index 3b480706625..e98b1628d23 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -30,7 +30,7 @@ await Promise.all([ fs.mkdir(Global.Path.bin, { recursive: true }), ]) -const CACHE_VERSION = "10" +const CACHE_VERSION = "11" const version = await Bun.file(path.join(Global.Path.cache, "version")) .text() diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index ff07e68a7ff..7d1f50ec8b2 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -28,7 +28,7 @@ export namespace Plugin { } const plugins = [...(config.plugin ?? [])] if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) { - plugins.push("opencode-copilot-auth@0.0.5") + plugins.push("opencode-copilot-auth@0.0.7") plugins.push("opencode-anthropic-auth@0.0.2") } for (let plugin of plugins) { diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index d632085cfc1..e6f7f5f888c 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -685,7 +685,14 @@ export namespace Provider { } } } - return getModel("opencode", "gpt-5-nano") + + // Check if opencode provider is available before using it + const opencodeProvider = await state().then((state) => state.providers["opencode"]) + if (opencodeProvider && opencodeProvider.info.models["gpt-5-nano"]) { + return getModel("opencode", "gpt-5-nano") + } + + return undefined } const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"] diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index ad04f7ef88c..6cf03fc06d0 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -272,4 +272,12 @@ export namespace ProviderTransform { return schema } + + export function error(providerID: string, message: string) { + if (providerID === "github-copilot" && message.includes("The requested model is not supported")) { + message += + "\n\nMake sure the model is enabled in your copilot settings: https://github.com/settings/copilot/features" + } + return message + } } diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 87cd77e1275..c451ae2b38d 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -8,6 +8,7 @@ import { LSP } from "../lsp" import { Snapshot } from "@/snapshot" import { fn } from "@/util/fn" import { Storage } from "@/storage/storage" +import { ProviderTransform } from "@/provider/transform" export namespace MessageV2 { export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({})) @@ -737,9 +738,10 @@ export namespace MessageV2 { { cause: e }, ).toObject() case APICallError.isInstance(e): + const message = ProviderTransform.error(ctx.providerID, e.message) return new MessageV2.APIError( { - message: e.message, + message, statusCode: e.statusCode, isRetryable: e.isRetryable, responseHeaders: e.responseHeaders, diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index f550005b341..b3c3c467168 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1408,7 +1408,8 @@ export namespace SessionPrompt { input.history.filter((m) => m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic)) .length === 1 if (!isFirst) return - const small = await Provider.getSmallModel(input.providerID) + const small = + (await Provider.getSmallModel(input.providerID)) ?? (await Provider.getModel(input.providerID, input.modelID)) const options = pipe( {}, mergeDeep(ProviderTransform.options(small.providerID, small.modelID, small.npm ?? "", input.session.id)), diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index de2ef2e5c92..d9247f182dc 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -73,7 +73,10 @@ export namespace SessionSummary { await Session.updateMessage(userMsg) const assistantMsg = messages.find((m) => m.info.role === "assistant")!.info as MessageV2.Assistant - const small = await Provider.getSmallModel(assistantMsg.providerID) + const small = + (await Provider.getSmallModel(assistantMsg.providerID)) ?? + (await Provider.getModel(assistantMsg.providerID, assistantMsg.modelID)) + const options = pipe( {}, mergeDeep(ProviderTransform.options(small.providerID, small.modelID, small.npm ?? "", assistantMsg.sessionID)), diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 46f4353dcbb..9f284756d9b 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.106", + "version": "1.0.107", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 3f77d8ca4fb..b43cd27837e 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.106", + "version": "1.0.107", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 2c2d7dbe4d0..9fda47a2fd1 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.106", + "version": "1.0.107", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index d7af0cab32f..8b7d4c98857 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.106", + "version": "1.0.107", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/ui/package.json b/packages/ui/package.json index 229bd80c2f0..8edf23bc375 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.106", + "version": "1.0.107", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index f1f242e0e0d..ea8be33cd3d 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.106", + "version": "1.0.107", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 99c93a9a445..4bcb0835aa2 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.106", + "version": "1.0.107", "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 760701bf1c3..db9f63ec691 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.106", + "version": "1.0.107", "publisher": "sst-dev", "repository": { "type": "git",