From 5f35c579e2ec53b472749f77e7754e5cb86597e4 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:46:22 -0600 Subject: [PATCH 01/35] fix: accordion styles --- packages/desktop/vite.config.ts | 1 - packages/ui/src/components/accordion.css | 26 +++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/desktop/vite.config.ts b/packages/desktop/vite.config.ts index cc552b02b..ff18b1de3 100644 --- a/packages/desktop/vite.config.ts +++ b/packages/desktop/vite.config.ts @@ -17,6 +17,5 @@ export default defineConfig({ }, build: { target: "esnext", - cssCodeSplit: false, }, }) diff --git a/packages/ui/src/components/accordion.css b/packages/ui/src/components/accordion.css index 8cfac06b0..5724307cd 100644 --- a/packages/ui/src/components/accordion.css +++ b/packages/ui/src/components/accordion.css @@ -74,18 +74,6 @@ border-bottom-right-radius: var(--radius-md); } - [data-slot="accordion-item"]:has(+ &) { - &[data-closed] { - border-bottom-left-radius: var(--radius-md); - border-bottom-right-radius: var(--radius-md); - [data-slot="accordion-trigger"] { - border-bottom-left-radius: var(--radius-md); - border-bottom-right-radius: var(--radius-md); - } - } - margin-bottom: 8px; - } - & + [data-slot="accordion-item"] { margin-top: 8px; @@ -96,6 +84,20 @@ } } + &:has(+ [data-slot="accordion-item"][data-expanded]) { + margin-bottom: 8px; + + &[data-closed] { + border-bottom-left-radius: var(--radius-md); + border-bottom-right-radius: var(--radius-md); + + [data-slot="accordion-trigger"] { + border-bottom-left-radius: var(--radius-md); + border-bottom-right-radius: var(--radius-md); + } + } + } + &[data-closed] + &[data-closed] { [data-slot="accordion-trigger"] { border-top: none; From 9eabbe2e76d3c837cc55ed14b6fdba80ebe602aa Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 25 Nov 2025 15:44:24 -0500 Subject: [PATCH 02/35] retry anthropic overloaded errors --- packages/opencode/src/session/processor.ts | 4 +- packages/opencode/src/session/retry.ts | 46 ++++++++++---------- packages/opencode/test/session/retry.test.ts | 18 ++++---- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 6d1125c66..5b45dc14d 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -333,9 +333,9 @@ export namespace SessionProcessor { error: e, }) const error = MessageV2.fromError(e, { providerID: input.providerID }) - if (error?.name === "APIError" && error.data.isRetryable) { + if ((error?.name === "APIError" && error.data.isRetryable) || error.data.message.includes("Overloaded")) { attempt++ - const delay = SessionRetry.delay(error, attempt) + const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined) SessionStatus.set(input.sessionID, { type: "retry", attempt, diff --git a/packages/opencode/src/session/retry.ts b/packages/opencode/src/session/retry.ts index 75472b568..4ad81ea08 100644 --- a/packages/opencode/src/session/retry.ts +++ b/packages/opencode/src/session/retry.ts @@ -19,32 +19,34 @@ export namespace SessionRetry { }) } - export function delay(error: MessageV2.APIError, attempt: number) { - const headers = error.data.responseHeaders - if (headers) { - const retryAfterMs = headers["retry-after-ms"] - if (retryAfterMs) { - const parsedMs = Number.parseFloat(retryAfterMs) - if (!Number.isNaN(parsedMs)) { - return parsedMs + export function delay(attempt: number, error?: MessageV2.APIError) { + if (error) { + const headers = error.data.responseHeaders + if (headers) { + const retryAfterMs = headers["retry-after-ms"] + if (retryAfterMs) { + const parsedMs = Number.parseFloat(retryAfterMs) + if (!Number.isNaN(parsedMs)) { + return parsedMs + } } - } - const retryAfter = headers["retry-after"] - if (retryAfter) { - const parsedSeconds = Number.parseFloat(retryAfter) - if (!Number.isNaN(parsedSeconds)) { - // convert seconds to milliseconds - return Math.ceil(parsedSeconds * 1000) + const retryAfter = headers["retry-after"] + if (retryAfter) { + const parsedSeconds = Number.parseFloat(retryAfter) + if (!Number.isNaN(parsedSeconds)) { + // convert seconds to milliseconds + return Math.ceil(parsedSeconds * 1000) + } + // Try parsing as HTTP date format + const parsed = Date.parse(retryAfter) - Date.now() + if (!Number.isNaN(parsed) && parsed > 0) { + return Math.ceil(parsed) + } } - // Try parsing as HTTP date format - const parsed = Date.parse(retryAfter) - Date.now() - if (!Number.isNaN(parsed) && parsed > 0) { - return Math.ceil(parsed) - } - } - return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1) + return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1) + } } return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index dc7470f0a..b685eae95 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -13,49 +13,49 @@ function apiError(headers?: Record): MessageV2.APIError { describe("session.retry.delay", () => { test("caps delay at 30 seconds when headers missing", () => { const error = apiError() - const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(error, index + 1)) + const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(index + 1, error)) expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 30000, 30000, 30000, 30000, 30000, 30000]) }) test("prefers retry-after-ms when shorter than exponential", () => { const error = apiError({ "retry-after-ms": "1500" }) - expect(SessionRetry.delay(error, 4)).toBe(1500) + expect(SessionRetry.delay(4, error)).toBe(1500) }) test("uses retry-after seconds when reasonable", () => { const error = apiError({ "retry-after": "30" }) - expect(SessionRetry.delay(error, 3)).toBe(30000) + expect(SessionRetry.delay(3, error)).toBe(30000) }) test("accepts http-date retry-after values", () => { const date = new Date(Date.now() + 20000).toUTCString() const error = apiError({ "retry-after": date }) - const d = SessionRetry.delay(error, 1) + const d = SessionRetry.delay(1, error) expect(d).toBeGreaterThanOrEqual(19000) expect(d).toBeLessThanOrEqual(20000) }) test("ignores invalid retry hints", () => { const error = apiError({ "retry-after": "not-a-number" }) - expect(SessionRetry.delay(error, 1)).toBe(2000) + expect(SessionRetry.delay(1, error)).toBe(2000) }) test("ignores malformed date retry hints", () => { const error = apiError({ "retry-after": "Invalid Date String" }) - expect(SessionRetry.delay(error, 1)).toBe(2000) + expect(SessionRetry.delay(1, error)).toBe(2000) }) test("ignores past date retry hints", () => { const pastDate = new Date(Date.now() - 5000).toUTCString() const error = apiError({ "retry-after": pastDate }) - expect(SessionRetry.delay(error, 1)).toBe(2000) + expect(SessionRetry.delay(1, error)).toBe(2000) }) test("uses retry-after values even when exceeding 10 minutes with headers", () => { const error = apiError({ "retry-after": "50" }) - expect(SessionRetry.delay(error, 1)).toBe(50000) + expect(SessionRetry.delay(1, error)).toBe(50000) const longError = apiError({ "retry-after-ms": "700000" }) - expect(SessionRetry.delay(longError, 1)).toBe(700000) + expect(SessionRetry.delay(1, longError)).toBe(700000) }) }) From ec1260d8aac6bbef3c54076614acd8a5e93bd82b Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 25 Nov 2025 20:54:40 +0000 Subject: [PATCH 03/35] release: v1.0.111 --- 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 171e06e58..e8255037e 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -47,7 +47,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -74,7 +74,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -98,7 +98,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -122,7 +122,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -163,7 +163,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -191,7 +191,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -207,7 +207,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.110", + "version": "1.0.111", "bin": { "opencode": "./bin/opencode", }, @@ -294,7 +294,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -314,7 +314,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.110", + "version": "1.0.111", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -325,7 +325,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -338,7 +338,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", @@ -351,7 +351,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -383,7 +383,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.110", + "version": "1.0.111", "dependencies": { "zod": "catalog:", }, @@ -393,7 +393,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.110", + "version": "1.0.111", "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 3a20b4752..89a822f5a 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.110", + "version": "1.0.111", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index c63e10282..ebd776e87 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.110", + "version": "1.0.111", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 82b2a2ed1..9ae2ceaab 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.110", + "version": "1.0.111", "$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 d263bad8e..82640aa78 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.110", + "version": "1.0.111", "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 93517f10c..80e7150d7 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.110", + "version": "1.0.111", "description": "", "type": "module", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index c562358d1..9691099b8 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.110", + "version": "1.0.111", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 336759b88..8ca9e0803 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.110" +version = "1.0.111" 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.110/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.111/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.110/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.111/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.110/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.111/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.110/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.111/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.110/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.111/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 4ee85fa6a..40d5b46ae 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.110", + "version": "1.0.111", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 1d61b56bf..d7d96f66b 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.110", + "version": "1.0.111", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 8b55b0689..a3be82bda 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.110", + "version": "1.0.111", "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 d65d93a4e..8b525478d 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.110", + "version": "1.0.111", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index 0b9d95c85..43d837a03 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.110", + "version": "1.0.111", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index 7950e47f3..a8ee64894 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.110", + "version": "1.0.111", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/ui/package.json b/packages/ui/package.json index e326c85a9..6d9978a7b 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.110", + "version": "1.0.111", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index df5fe757f..5270a5d5e 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.110", + "version": "1.0.111", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 5a7228392..458d7f067 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.110", + "version": "1.0.111", "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 7d4b81f98..434d220c2 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.110", + "version": "1.0.111", "publisher": "sst-dev", "repository": { "type": "git", From 125938c7a1e4a74af22d9e1bbbe5c7e1c0d7d72f Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 25 Nov 2025 21:10:30 +0000 Subject: [PATCH 04/35] release: v1.0.112 --- 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 | 2 +- packages/sdk/js/package.json | 2 +- 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, 37 insertions(+), 37 deletions(-) diff --git a/bun.lock b/bun.lock index e8255037e..b81a80cfe 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -47,7 +47,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -74,7 +74,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -98,7 +98,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -122,7 +122,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -163,7 +163,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -191,7 +191,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -207,7 +207,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.111", + "version": "1.0.112", "bin": { "opencode": "./bin/opencode", }, @@ -294,7 +294,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -314,7 +314,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.111", + "version": "1.0.112", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -325,7 +325,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -338,7 +338,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", @@ -351,7 +351,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -383,7 +383,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.111", + "version": "1.0.112", "dependencies": { "zod": "catalog:", }, @@ -393,7 +393,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.111", + "version": "1.0.112", "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 89a822f5a..d86cc873a 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.111", + "version": "1.0.112", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ebd776e87..61968d1cf 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.111", + "version": "1.0.112", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 9ae2ceaab..ec5a0371c 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.111", + "version": "1.0.112", "$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 82640aa78..89eedaeef 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.111", + "version": "1.0.112", "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 80e7150d7..fb51dc1e1 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.111", + "version": "1.0.112", "description": "", "type": "module", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 9691099b8..3199f75d2 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.111", + "version": "1.0.112", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 8ca9e0803..7bdcfcc75 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.111" +version = "1.0.112" 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.111/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.112/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.111/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.112/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.111/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.112/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.111/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.112/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.111/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.112/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 40d5b46ae..93ae9e630 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.111", + "version": "1.0.112", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d7d96f66b..aabc98ae4 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.111", + "version": "1.0.112", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index a3be82bda..7e55a26ab 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.111", + "version": "1.0.112", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 8b525478d..2972b63e3 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.111", + "version": "1.0.112", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 43d837a03..f0e879331 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.111", + "version": "1.0.112", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index a8ee64894..8191fb8b4 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.111", + "version": "1.0.112", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/ui/package.json b/packages/ui/package.json index 6d9978a7b..e8ae9ea7c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.111", + "version": "1.0.112", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index 5270a5d5e..a573fac3c 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.111", + "version": "1.0.112", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 458d7f067..c799d6410 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.111", + "version": "1.0.112", "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 434d220c2..5d206a627 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.111", + "version": "1.0.112", "publisher": "sst-dev", "repository": { "type": "git", From eb04cdac412c5e69281cfffbb1ef7a1f56f2dd0d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 25 Nov 2025 16:03:20 -0500 Subject: [PATCH 05/35] better overloaded message --- packages/opencode/src/session/processor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 5b45dc14d..5bd833c0f 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -339,7 +339,7 @@ export namespace SessionProcessor { SessionStatus.set(input.sessionID, { type: "retry", attempt, - message: error.data.message, + message: error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message, next: Date.now() + delay, }) await SessionRetry.sleep(delay, input.abort).catch(() => {}) From f8996f0a90a5d08748090eb753b55f0a66e71dc1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 25 Nov 2025 21:09:47 +0000 Subject: [PATCH 06/35] chore: format code --- 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 7e55a26ab..e7f3b4064 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 2972b63e3..1173d9dfa 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 2b05fe28595c7c636b4781b8b893c6e6755c1943 Mon Sep 17 00:00:00 2001 From: Jaga Santagostino Date: Tue, 25 Nov 2025 22:27:28 +0100 Subject: [PATCH 07/35] docs: improve notes on mobile (#4747) --- packages/web/src/styles/custom.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/web/src/styles/custom.css b/packages/web/src/styles/custom.css index de4fae0ad..04331dd6a 100644 --- a/packages/web/src/styles/custom.css +++ b/packages/web/src/styles/custom.css @@ -215,6 +215,11 @@ site-search > button span { .starlight-aside__content { margin-top: 0; } + + @media (max-width: 768px) { + flex-direction: column; + gap: 6px; + } } site-search > button > kbd { From 79a7edea5e9a0fdb4f9f905c1ba968fd84c543e0 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 25 Nov 2025 14:57:07 -0600 Subject: [PATCH 08/35] ci: update changelog prompt --- script/publish.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/script/publish.ts b/script/publish.ts index 2d20057d2..a97a98043 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -50,6 +50,7 @@ if (!Script.preview) { - Do NOT include any information about code changes if they do not affect the user facing changes. - For commits that are already well-written and descriptive, avoid rewording them. Simply capitalize the first letter, fix any misspellings, and ensure proper English grammar. - DO NOT read any other commits than the ones listed above (THIS IS IMPORTANT TO AVOID DUPLICATING THINGS IN OUR CHANGELOG) + - If a commit was made and then reverted do not include it in the changelog. If the commits only include a revert but not the original commit, then include the revert in the changelog. IMPORTANT: ONLY return a bulleted list of changes, do not include any other information. Do not include a preamble like "Based on my analysis..." From e404bf33b1b589a0a615c746f245f92b6836ba39 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 25 Nov 2025 15:35:10 -0600 Subject: [PATCH 09/35] update install script to handle musl & avx --- install | 81 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/install b/install index f60a72642..77ecf34b9 100755 --- a/install +++ b/install @@ -11,43 +11,82 @@ requested_version=${VERSION:-} raw_os=$(uname -s) os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]') -# Normalize various Unix-like identifiers case "$raw_os" in Darwin*) os="darwin" ;; Linux*) os="linux" ;; MINGW*|MSYS*|CYGWIN*) os="windows" ;; - esac -arch=$(uname -m) +esac +arch=$(uname -m) if [[ "$arch" == "aarch64" ]]; then arch="arm64" -elif [[ "$arch" == "x86_64" ]]; then +fi +if [[ "$arch" == "x86_64" ]]; then arch="x64" fi -if [ "$os" = "linux" ]; then - filename="$APP-$os-$arch.tar.gz" -else - filename="$APP-$os-$arch.zip" +if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then + rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0) + if [ "$rosetta_flag" = "1" ]; then + arch="arm64" + fi fi - -case "$filename" in - *"-linux-"*) - [[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1 +combo="$os-$arch" +case "$combo" in + linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64) ;; - *"-darwin-"*) - [[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1 - ;; - *"-windows-"*) - [[ "$arch" == "x64" ]] || exit 1 - ;; - *) - echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}" - exit 1 + *) + echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}" + exit 1 ;; esac +archive_ext=".zip" +if [ "$os" = "linux" ]; then + archive_ext=".tar.gz" +fi + +is_musl=false +if [ "$os" = "linux" ]; then + if [ -f /etc/alpine-release ]; then + is_musl=true + fi + + if command -v ldd >/dev/null 2>&1; then + if ldd --version 2>&1 | grep -qi musl; then + is_musl=true + fi + fi +fi + +needs_baseline=false +if [ "$arch" = "x64" ]; then + if [ "$os" = "linux" ]; then + if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then + needs_baseline=true + fi + fi + + if [ "$os" = "darwin" ]; then + avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0) + if [ "$avx2" != "1" ]; then + needs_baseline=true + fi + fi +fi + +target="$os-$arch" +if [ "$needs_baseline" = "true" ]; then + target="$target-baseline" +fi +if [ "$is_musl" = "true" ]; then + target="$target-musl" +fi + +filename="$APP-$target$archive_ext" + + if [ "$os" = "linux" ]; then if ! command -v tar >/dev/null 2>&1; then echo -e "${RED}Error: 'tar' is required but not installed.${NC}" From eaeea45ace01e20405d49d1a659030a4d131dc83 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 25 Nov 2025 15:36:26 -0600 Subject: [PATCH 10/35] ci: change changelog model --- script/publish.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/publish.ts b/script/publish.ts index a97a98043..364a1d217 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -35,7 +35,7 @@ if (!Script.preview) { body: { model: { providerID: "opencode", - modelID: "kimi-k2", + modelID: "claude-haiku-4-5", }, parts: [ { From 447713244820e875e192a5815c4d8bc76c03b40f Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:02:27 -0600 Subject: [PATCH 11/35] fix: sanitize absolute paths --- packages/desktop/src/components/file-tree.tsx | 3 +- .../desktop/src/components/prompt-input.tsx | 2 +- packages/desktop/src/context/sync.tsx | 32 +- .../desktop/src/pages/directory-layout.tsx | 2 +- packages/desktop/src/pages/home.tsx | 3 +- packages/desktop/src/pages/layout.tsx | 3 +- packages/desktop/src/pages/session.tsx | 2 +- packages/desktop/src/ui/collapsible.tsx | 62 --- packages/desktop/src/ui/index.ts | 6 - packages/desktop/src/utils/index.ts | 1 - packages/desktop/src/utils/path.ts | 20 - .../enterprise/src/routes/share/[shareID].tsx | 409 +++++++++--------- packages/ui/src/components/message-part.css | 11 + packages/ui/src/components/message-part.tsx | 73 ++-- .../ui/src/components/message-progress.tsx | 3 +- packages/ui/src/components/session-turn.tsx | 6 +- packages/ui/src/context/data.tsx | 4 +- packages/util/src/sanitize.ts | 28 ++ 18 files changed, 312 insertions(+), 358 deletions(-) delete mode 100644 packages/desktop/src/ui/collapsible.tsx delete mode 100644 packages/desktop/src/ui/index.ts delete mode 100644 packages/desktop/src/utils/path.ts create mode 100644 packages/util/src/sanitize.ts diff --git a/packages/desktop/src/components/file-tree.tsx b/packages/desktop/src/components/file-tree.tsx index f3729a8d3..0841c71d1 100644 --- a/packages/desktop/src/components/file-tree.tsx +++ b/packages/desktop/src/components/file-tree.tsx @@ -1,5 +1,5 @@ import { useLocal, type LocalFile } from "@/context/local" -import { Collapsible } from "@/ui" +import { Collapsible } from "@opencode-ai/ui/collapsible" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Tooltip } from "@opencode-ai/ui/tooltip" import { For, Match, Switch, Show, type ComponentProps, type ParentProps } from "solid-js" @@ -76,6 +76,7 @@ export default function FileTree(props: { p())).then(() => setStore("ready", true)) - const sanitizer = createMemo(() => new RegExp(`${store.path.directory}/`, "g")) - const sanitize = (text: string) => text.replace(sanitizer(), "") const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/") - const sanitizePart = (part: Part) => { - if (part.type === "tool") { - if (part.state.status === "completed" || part.state.status === "error") { - for (const key in part.state.metadata) { - if (typeof part.state.metadata[key] === "string") { - part.state.metadata[key] = sanitize(part.state.metadata[key] as string) - } - } - for (const key in part.state.input) { - if (typeof part.state.input[key] === "string") { - part.state.input[key] = sanitize(part.state.input[key] as string) - } - } - if ("error" in part.state) { - part.state.error = sanitize(part.state.error as string) - } - } - } - return part - } return { data: store, @@ -88,10 +65,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ .slice() .sort((a, b) => a.id.localeCompare(b.id)) for (const message of messages.data!) { - draft.part[message.info.id] = message.parts - .slice() - .map(sanitizePart) - .sort((a, b) => a.id.localeCompare(b.id)) + draft.part[message.info.id] = message.parts.slice().sort((a, b) => a.id.localeCompare(b.id)) } draft.session_diff[sessionID] = diff.data ?? [] }), @@ -105,7 +79,9 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, load, absolute, - sanitize, + get directory() { + return store.path.directory + }, } }, }) diff --git a/packages/desktop/src/pages/directory-layout.tsx b/packages/desktop/src/pages/directory-layout.tsx index 2fe750fda..de16eff30 100644 --- a/packages/desktop/src/pages/directory-layout.tsx +++ b/packages/desktop/src/pages/directory-layout.tsx @@ -21,7 +21,7 @@ export default function Layout(props: ParentProps) { {iife(() => { const sync = useSync() return ( - + {props.children} ) diff --git a/packages/desktop/src/pages/home.tsx b/packages/desktop/src/pages/home.tsx index e773fff57..58fcb20ce 100644 --- a/packages/desktop/src/pages/home.tsx +++ b/packages/desktop/src/pages/home.tsx @@ -1,8 +1,9 @@ import { useGlobalSync } from "@/context/global-sync" -import { base64Encode, getFilename } from "@/utils" +import { base64Encode } from "@/utils" import { For } from "solid-js" import { A } from "@solidjs/router" import { Button } from "@opencode-ai/ui/button" +import { getFilename } from "@opencode-ai/util/path" export default function Home() { const sync = useGlobalSync() diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index c9bb559d8..15180c885 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -3,7 +3,7 @@ import { DateTime } from "luxon" import { A, useParams } from "@solidjs/router" import { useLayout } from "@/context/layout" import { useGlobalSync } from "@/context/global-sync" -import { base64Encode, getFilename } from "@/utils" +import { base64Encode } from "@/utils" import { Mark } from "@opencode-ai/ui/logo" import { Button } from "@opencode-ai/ui/button" import { Icon } from "@opencode-ai/ui/icon" @@ -11,6 +11,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip } from "@opencode-ai/ui/tooltip" import { Collapsible } from "@opencode-ai/ui/collapsible" import { DiffChanges } from "@opencode-ai/ui/diff-changes" +import { getFilename } from "@opencode-ai/util/path" export default function Layout(props: ParentProps) { const params = useParams() diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 0eb0f37ce..40acac663 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -1,7 +1,6 @@ import { For, onCleanup, onMount, Show, Match, Switch, createResource, createMemo } from "solid-js" import { useLocal, type LocalFile } from "@/context/local" import { createStore } from "solid-js/store" -import { getDirectory, getFilename } from "@/utils" import { PromptInput } from "@/components/prompt-input" import { DateTime } from "luxon" import { FileIcon } from "@opencode-ai/ui/file-icon" @@ -30,6 +29,7 @@ import type { JSX } from "solid-js" import { useSync } from "@/context/sync" import { useSession } from "@/context/session" import { useLayout } from "@/context/layout" +import { getDirectory, getFilename } from "@opencode-ai/util/path" export default function Page() { const layout = useLayout() diff --git a/packages/desktop/src/ui/collapsible.tsx b/packages/desktop/src/ui/collapsible.tsx deleted file mode 100644 index 5fbb6c7a4..000000000 --- a/packages/desktop/src/ui/collapsible.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Collapsible as KobalteCollapsible } from "@kobalte/core/collapsible" -import { Icon, IconProps } from "@opencode-ai/ui/icon" -import { splitProps } from "solid-js" -import type { ComponentProps, ParentProps } from "solid-js" - -export interface CollapsibleProps extends ComponentProps {} -export interface CollapsibleTriggerProps extends ComponentProps {} -export interface CollapsibleContentProps extends ComponentProps {} - -function CollapsibleRoot(props: CollapsibleProps) { - return -} - -function CollapsibleTrigger(props: CollapsibleTriggerProps) { - const [local, others] = splitProps(props, ["class"]) - return ( - - ) -} - -function CollapsibleContent(props: ParentProps) { - const [local, others] = splitProps(props, ["class", "children"]) - return ( - - {local.children} - - ) -} - -function CollapsibleArrow(props: Partial) { - const [local, others] = splitProps(props, ["class", "name"]) - return ( - - ) -} - -export const Collapsible = Object.assign(CollapsibleRoot, { - Trigger: CollapsibleTrigger, - Content: CollapsibleContent, - Arrow: CollapsibleArrow, -}) diff --git a/packages/desktop/src/ui/index.ts b/packages/desktop/src/ui/index.ts deleted file mode 100644 index 8cbf0834f..000000000 --- a/packages/desktop/src/ui/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - Collapsible, - type CollapsibleProps, - type CollapsibleTriggerProps, - type CollapsibleContentProps, -} from "./collapsible" diff --git a/packages/desktop/src/utils/index.ts b/packages/desktop/src/utils/index.ts index 63a656cc4..e50efe837 100644 --- a/packages/desktop/src/utils/index.ts +++ b/packages/desktop/src/utils/index.ts @@ -1,3 +1,2 @@ -export * from "./path" export * from "./dom" export * from "./encode" diff --git a/packages/desktop/src/utils/path.ts b/packages/desktop/src/utils/path.ts deleted file mode 100644 index d23568ae6..000000000 --- a/packages/desktop/src/utils/path.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useSync } from "@/context/sync" - -export function getFilename(path: string) { - if (!path) return "" - const trimmed = path.replace(/[\/]+$/, "") - const parts = trimmed.split("/") - return parts[parts.length - 1] ?? "" -} - -export function getDirectory(path: string) { - const sync = useSync() - const parts = path.split("/") - const dir = parts.slice(0, parts.length - 1).join("/") - return dir ? sync.sanitize(dir + "/") : "" -} - -export function getFileExtension(path: string) { - const parts = path.split(".") - return parts[parts.length - 1] -} diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 292038094..42dd7e957 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -141,219 +141,226 @@ export default function () { }} > - {(data) => ( - - {iife(() => { - const [store, setStore] = createStore({ - messageId: undefined as string | undefined, - }) - const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id)) - if (!match().found) throw new Error(`Session ${data().sessionID} not found`) - const info = createMemo(() => data().session[match().index]) - const messages = createMemo(() => - data().sessionID - ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort( - (a, b) => b.time.created - a.time.created, - ) - : [], - ) - const firstUserMessage = createMemo(() => messages().at(0)) - const activeMessage = createMemo( - () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(), - ) - function setActiveMessage(message: UserMessage | undefined) { - if (message) { - setStore("messageId", message.id) - } else { - setStore("messageId", undefined) + {(data) => { + const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id)) + if (!match().found) throw new Error(`Session ${data().sessionID} not found`) + const info = createMemo(() => data().session[match().index]) + + return ( + + {iife(() => { + const [store, setStore] = createStore({ + messageId: undefined as string | undefined, + }) + const messages = createMemo(() => + data().sessionID + ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort( + (a, b) => b.time.created - a.time.created, + ) + : [], + ) + const firstUserMessage = createMemo(() => messages().at(0)) + const activeMessage = createMemo( + () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(), + ) + function setActiveMessage(message: UserMessage | undefined) { + if (message) { + setStore("messageId", message.id) + } else { + setStore("messageId", undefined) + } } - } - const provider = createMemo(() => activeMessage()?.model?.providerID) - const modelID = createMemo(() => activeMessage()?.model?.modelID) - const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID())) - const diffs = createMemo(() => { - const diffs = data().session_diff[data().sessionID] ?? [] - const preloaded = data().session_diff_preload[data().sessionID] ?? [] - return diffs.map((diff) => ({ - ...diff, - preloaded: preloaded.find((d) => d.newFile.name === diff.file), - })) - }) + const provider = createMemo(() => activeMessage()?.model?.providerID) + const modelID = createMemo(() => activeMessage()?.model?.modelID) + const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID())) + const diffs = createMemo(() => { + const diffs = data().session_diff[data().sessionID] ?? [] + const preloaded = data().session_diff_preload[data().sessionID] ?? [] + return diffs.map((diff) => ({ + ...diff, + preloaded: preloaded.find((d) => d.newFile.name === diff.file), + })) + }) - const title = () => ( -
-
-
- -
v{info().version}
-
-
- -
{model()?.name ?? modelID()}
-
-
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} + const title = () => ( +
+
+
+ +
v{info().version}
+
+
+ +
{model()?.name ?? modelID()}
+
+
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
+
{info().title}
-
{info().title}
-
- ) + ) - const turns = () => ( -
- {title()} -
- - {(message) => ( - - )} - -
-
- -
-
- ) - - const wide = createMemo(() => diffs().length === 0) - - return ( -
-
-
- - - -
-
- - -
-
-
- - ) - })} - - )} + ) + + const wide = createMemo(() => diffs().length === 0) + + return ( +
+
+
+ + + +
+
+ + +
+
+
+ + + 0}> + + + + Session + + + 5 Files Changed + + + + {turns()} + + + + + +
{turns()}
+
+
+
+
+ ) + })} + + ) + }} ) diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index dd6166112..0b1e8d490 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -63,6 +63,17 @@ [data-component="tool-output"] { white-space: pre; + padding: 8px 12px; + height: fit-content; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + + pre { + margin: 0; + padding: 0; + } } [data-component="edit-trigger"], diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index dd920f101..40740fa1f 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -16,35 +16,26 @@ import { Checkbox } from "./checkbox" import { Diff } from "./diff" import { DiffChanges } from "./diff-changes" import { Markdown } from "./markdown" +import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { sanitize, sanitizePart } from "@opencode-ai/util/sanitize" export interface MessageProps { message: MessageType parts: PartType[] + sanitize?: RegExp } export interface MessagePartProps { part: PartType message: MessageType hideDetails?: boolean + sanitize?: RegExp } export type PartComponent = Component export const PART_MAPPING: Record = {} -function getFilename(path: string) { - if (!path) return "" - const trimmed = path.replace(/[\/]+$/, "") - const parts = trimmed.split("/") - return parts[parts.length - 1] ?? "" -} - -function getDirectory(path: string) { - const parts = path.split("/") - const dir = parts.slice(0, parts.length - 1).join("/") - return dir ? dir + "/" : "" -} - export function registerPartComponent(type: string, component: PartComponent) { PART_MAPPING[type] = component } @@ -57,21 +48,27 @@ export function Message(props: MessageProps) { {(assistantMessage) => ( - + )} ) } -export function AssistantMessageDisplay(props: { message: AssistantMessage; parts: PartType[] }) { +export function AssistantMessageDisplay(props: { message: AssistantMessage; parts: PartType[]; sanitize?: RegExp }) { const filteredParts = createMemo(() => { return props.parts?.filter((x) => { if (x.type === "reasoning") return false return x.type !== "tool" || (x as ToolPart).tool !== "todoread" }) }) - return {(part) => } + return ( + {(part) => } + ) } export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[] }) { @@ -88,7 +85,13 @@ export function Part(props: MessagePartProps) { const component = createMemo(() => PART_MAPPING[props.part.type]) return ( - + ) } @@ -99,6 +102,7 @@ export interface ToolProps { tool: string output?: string hideDetails?: boolean + sanitize?: RegExp } export type ToolComponent = Component @@ -166,6 +170,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { metadata={metadata} output={part.state.status === "completed" ? part.state.output : undefined} hideDetails={props.hideDetails} + sanitize={props.sanitize} /> @@ -177,10 +182,11 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { PART_MAPPING["text"] = function TextPartDisplay(props) { const part = props.part as TextPart + const sanitized = createMemo(() => (props.sanitize ? (sanitizePart(part, props.sanitize) as TextPart) : part)) return (
- +
) @@ -205,7 +211,7 @@ ToolRegistry.register({ icon="glasses" trigger={{ title: "Read", - subtitle: props.input.filePath ? getFilename(props.input.filePath) : "", + subtitle: props.input.filePath ? getFilename(sanitize(props.input.filePath, props.sanitize)) : "", }} /> ) @@ -216,9 +222,12 @@ ToolRegistry.register({ name: "list", render(props) { return ( - + -
{props.output}
+
{sanitize(props.output, props.sanitize)}
) @@ -321,12 +330,14 @@ ToolRegistry.register({ icon="console" trigger={{ title: "Shell", - subtitle: "Ran " + props.input.command, + subtitle: props.input.description, }} > - -
{props.output}
-
+
+ +
) }, @@ -344,9 +355,13 @@ ToolRegistry.register({
Edit
- {getDirectory(props.input.filePath!)} + + {getDirectory(sanitize(props.input.filePath!, props.sanitize))} + - {getFilename(props.input.filePath ?? "")} + + {getFilename(sanitize(props.input.filePath ?? "", props.sanitize))} +
@@ -361,11 +376,11 @@ ToolRegistry.register({
diff --git a/packages/ui/src/components/message-progress.tsx b/packages/ui/src/components/message-progress.tsx index ca42d26ec..adb245ab4 100644 --- a/packages/ui/src/components/message-progress.tsx +++ b/packages/ui/src/components/message-progress.tsx @@ -6,6 +6,7 @@ import type { AssistantMessage as AssistantMessageType, ToolPart } from "@openco export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) { const data = useData() + const sanitizer = createMemo(() => (data.directory ? new RegExp(`${data.directory}/`, "g") : undefined)) const parts = createMemo(() => props.assistantMessages().flatMap((m) => data.part[m.id])) const done = createMemo(() => props.done ?? false) const currentTask = createMemo( @@ -152,7 +153,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa ) return (
- +
) }} diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index a7bd456a4..d146bae95 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -31,6 +31,7 @@ export function SessionTurn( const match = Binary.search(data.session, props.sessionID, (s) => s.id) if (!match.found) throw new Error(`Session ${props.sessionID} not found`) + const sanitizer = createMemo(() => (data.directory ? new RegExp(`${data.directory}/`, "g") : undefined)) const messages = createMemo(() => (props.sessionID ? (data.message[props.sessionID] ?? []) : [])) const userMessages = createMemo(() => messages() @@ -116,7 +117,7 @@ export function SessionTurn(
- +
{/* Summary */} @@ -222,10 +223,11 @@ export function SessionTurn( p?.id !== last()?.id)} + sanitize={sanitizer()} /> ) } - return + return }} diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx index c2766a5af..ad5a212ab 100644 --- a/packages/ui/src/context/data.tsx +++ b/packages/ui/src/context/data.tsx @@ -23,7 +23,7 @@ type Data = { export const { use: useData, provider: DataProvider } = createSimpleContext({ name: "Data", - init: (props: { data: Data }) => { - return props.data + init: (props: { data: Data; directory: string }) => { + return { ...props.data, directory: props.directory } }, }) diff --git a/packages/util/src/sanitize.ts b/packages/util/src/sanitize.ts new file mode 100644 index 000000000..892fe87cf --- /dev/null +++ b/packages/util/src/sanitize.ts @@ -0,0 +1,28 @@ +import { Part } from "@opencode-ai/sdk" + +export const sanitize = (text: string | undefined, remove?: RegExp) => (remove ? text?.replace(remove, "") : text) ?? "" + +export const sanitizePart = (part: Part, remove: RegExp) => { + if (part.type === "text") { + part.text = sanitize(part.text, remove) + } else if (part.type === "reasoning") { + part.text = sanitize(part.text, remove) + } else if (part.type === "tool") { + if (part.state.status === "completed" || part.state.status === "error") { + for (const key in part.state.metadata) { + if (typeof part.state.metadata[key] === "string") { + part.state.metadata[key] = sanitize(part.state.metadata[key] as string, remove) + } + } + for (const key in part.state.input) { + if (typeof part.state.input[key] === "string") { + part.state.input[key] = sanitize(part.state.input[key] as string, remove) + } + } + if ("error" in part.state) { + part.state.error = sanitize(part.state.error as string, remove) + } + } + } + return part +} From 00d7aed7978dc08e802ba0d7f0bc5c4fd6f36362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haris=20Gu=C5=A1i=C4=87?= Date: Tue, 25 Nov 2025 23:27:06 +0100 Subject: [PATCH 12/35] fix: prompt submit error with `opencode -c` (#4496) Co-authored-by: Aiden Cline --- packages/opencode/src/cli/cmd/tui/app.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 33be73ca2..f72dc8365 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -186,16 +186,13 @@ function App() { }) }) + let continued = false createEffect(() => { - if (sync.status !== "complete") return - if (args.continue) { - const match = sync.data.session.at(0)?.id - if (match) { - route.navigate({ - type: "session", - sessionID: match, - }) - } + if (continued || sync.status !== "complete" || !args.continue) return + const match = sync.data.session.at(0)?.id + if (match) { + continued = true + route.navigate({ type: "session", sessionID: match }) } }) From 19c8654195a7b27f974de3abefa9ef00a7cd7c1b Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:04:14 -0600 Subject: [PATCH 13/35] fix: missing deps --- packages/util/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/util/package.json b/packages/util/package.json index a573fac3c..7855f11f3 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -13,6 +13,7 @@ "zod": "catalog:" }, "devDependencies": { - "typescript": "catalog:" + "typescript": "catalog:", + "@types/node": "catalog:" } } From e8ac4a1e995ae57795e639115289e91851805a21 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:27:53 -0600 Subject: [PATCH 14/35] fix: build error --- packages/util/package.json | 3 +-- packages/util/src/sanitize.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/util/package.json b/packages/util/package.json index 7855f11f3..a573fac3c 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -13,7 +13,6 @@ "zod": "catalog:" }, "devDependencies": { - "typescript": "catalog:", - "@types/node": "catalog:" + "typescript": "catalog:" } } diff --git a/packages/util/src/sanitize.ts b/packages/util/src/sanitize.ts index 892fe87cf..270b618ae 100644 --- a/packages/util/src/sanitize.ts +++ b/packages/util/src/sanitize.ts @@ -1,4 +1,4 @@ -import { Part } from "@opencode-ai/sdk" +import type { Part } from "@opencode-ai/sdk/client" export const sanitize = (text: string | undefined, remove?: RegExp) => (remove ? text?.replace(remove, "") : text) ?? "" From 64caeeb12d88c9200cba4139ef8288f26c9a3a49 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Tue, 25 Nov 2025 23:35:34 +0100 Subject: [PATCH 15/35] fix(tui): abort in-progress generation on undo (#4744) --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 f8526e72b..2fb4aa556 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -319,7 +319,9 @@ export function Session() { value: "session.undo", keybind: "messages_undo", category: "Session", - onSelect: (dialog) => { + onSelect: async (dialog) => { + const status = sync.data.session_status[route.sessionID] + if (status?.type !== "idle") await sdk.client.session.abort({ path: { id: route.sessionID } }).catch(() => {}) const revert = session().revert?.messageID const message = messages().findLast((x) => (!revert || x.id < revert) && x.role === "user") if (!message) return From b62c7943e744fb5ce3c30a9f61b77ecb0efd0d40 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 25 Nov 2025 17:57:15 -0500 Subject: [PATCH 16/35] zen: trial --- .../app/src/routes/zen/util/handler.ts | 43 +- .../app/src/routes/zen/util/trialLimiter.ts | 42 + .../core/migrations/0038_famous_magik.sql | 8 + .../core/migrations/meta/0038_snapshot.json | 1037 +++++++++++++++++ .../core/migrations/meta/_journal.json | 9 +- packages/console/core/src/model.ts | 6 + packages/console/core/src/schema/ip.sql.ts | 12 + 7 files changed, 1149 insertions(+), 8 deletions(-) create mode 100644 packages/console/app/src/routes/zen/util/trialLimiter.ts create mode 100644 packages/console/core/migrations/0038_famous_magik.sql create mode 100644 packages/console/core/migrations/meta/0038_snapshot.json create mode 100644 packages/console/core/src/schema/ip.sql.ts diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index 330bcc1cf..239300c70 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -13,13 +13,20 @@ import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js" import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js" import { logger } from "./logger" import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error" -import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider" +import { + createBodyConverter, + createStreamPartConverter, + createResponseConverter, + ProviderHelper, + UsageInfo, +} from "./provider/provider" import { anthropicHelper } from "./provider/anthropic" import { googleHelper } from "./provider/google" import { openaiHelper } from "./provider/openai" import { oaCompatHelper } from "./provider/openai-compatible" import { createRateLimiter } from "./rateLimiter" import { createDataDumper } from "./dataDumper" +import { createTrialLimiter } from "./trialLimiter" type ZenData = Awaited> type RetryOptions = { @@ -62,11 +69,13 @@ export async function handler( const zenData = ZenData.list() const modelInfo = validateModel(zenData, model) const dataDumper = createDataDumper(sessionId, requestId) + const trialLimiter = createTrialLimiter(modelInfo.trial?.limit, ip) + const isTrial = await trialLimiter?.isTrial() const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip) await rateLimiter?.check() const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => { - const providerInfo = selectProvider(zenData, modelInfo, sessionId, retry) + const providerInfo = selectProvider(zenData, modelInfo, sessionId, isTrial ?? false, retry) const authInfo = await authenticate(modelInfo, providerInfo) validateBilling(authInfo, modelInfo) validateModelSettings(authInfo) @@ -136,8 +145,10 @@ export async function handler( logger.debug("RESPONSE: " + body) dataDumper?.provideResponse(body) dataDumper?.flush() + const tokensInfo = providerInfo.normalizeUsage(json.usage) + await trialLimiter?.track(tokensInfo) await rateLimiter?.track() - await trackUsage(authInfo, modelInfo, providerInfo, json.usage) + await trackUsage(authInfo, modelInfo, providerInfo, tokensInfo) await reload(authInfo) return new Response(body, { status: res.status, @@ -169,7 +180,9 @@ export async function handler( await rateLimiter?.track() const usage = usageParser.retrieve() if (usage) { - await trackUsage(authInfo, modelInfo, providerInfo, usage) + const tokensInfo = providerInfo.normalizeUsage(usage) + await trialLimiter?.track(tokensInfo) + await trackUsage(authInfo, modelInfo, providerInfo, tokensInfo) await reload(authInfo) } c.close() @@ -275,8 +288,19 @@ export async function handler( return { id: modelId, ...modelData } } - function selectProvider(zenData: ZenData, modelInfo: ModelInfo, sessionId: string, retry: RetryOptions) { + function selectProvider( + zenData: ZenData, + modelInfo: ModelInfo, + sessionId: string, + isTrial: boolean, + retry: RetryOptions, + ) { const provider = (() => { + // temporarily commment out + //if (isTrial) { + // return modelInfo.providers.find((provider) => provider.id === modelInfo.trial!.provider) + //} + if (retry.retryCount === MAX_RETRIES) { return modelInfo.providers.find((provider) => provider.id === modelInfo.fallbackProvider) } @@ -432,9 +456,14 @@ export async function handler( providerInfo.apiKey = authInfo.provider.credentials } - async function trackUsage(authInfo: AuthInfo, modelInfo: ModelInfo, providerInfo: ProviderInfo, usage: any) { + async function trackUsage( + authInfo: AuthInfo, + modelInfo: ModelInfo, + providerInfo: ProviderInfo, + usageInfo: UsageInfo, + ) { const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } = - providerInfo.normalizeUsage(usage) + usageInfo const modelCost = modelInfo.cost200K && diff --git a/packages/console/app/src/routes/zen/util/trialLimiter.ts b/packages/console/app/src/routes/zen/util/trialLimiter.ts new file mode 100644 index 000000000..82debbb73 --- /dev/null +++ b/packages/console/app/src/routes/zen/util/trialLimiter.ts @@ -0,0 +1,42 @@ +import { Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js" +import { IpTable } from "@opencode-ai/console-core/schema/ip.sql.js" +import { UsageInfo } from "./provider/provider" + +export function createTrialLimiter(limit: number | undefined, ip: string) { + if (!limit) return + + let trial: boolean + + return { + isTrial: async () => { + const data = await Database.use((tx) => + tx + .select({ + usage: IpTable.usage, + }) + .from(IpTable) + .where(eq(IpTable.ip, ip)) + .then((rows) => rows[0]), + ) + + trial = (data?.usage ?? 0) < limit + return trial + }, + track: async (usageInfo: UsageInfo) => { + if (!trial) return + const usage = + usageInfo.inputTokens + + usageInfo.outputTokens + + (usageInfo.reasoningTokens ?? 0) + + (usageInfo.cacheReadTokens ?? 0) + + (usageInfo.cacheWrite5mTokens ?? 0) + + (usageInfo.cacheWrite1hTokens ?? 0) + await Database.use((tx) => + tx + .insert(IpTable) + .values({ ip, usage }) + .onDuplicateKeyUpdate({ set: { usage: sql`${IpTable.usage} + ${usage}` } }), + ) + }, + } +} diff --git a/packages/console/core/migrations/0038_famous_magik.sql b/packages/console/core/migrations/0038_famous_magik.sql new file mode 100644 index 000000000..ad195f555 --- /dev/null +++ b/packages/console/core/migrations/0038_famous_magik.sql @@ -0,0 +1,8 @@ +CREATE TABLE `ip` ( + `ip` varchar(45) NOT NULL, + `time_created` timestamp(3) NOT NULL DEFAULT (now()), + `time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + `time_deleted` timestamp(3), + `usage` int, + CONSTRAINT `ip_ip_pk` PRIMARY KEY(`ip`) +); diff --git a/packages/console/core/migrations/meta/0038_snapshot.json b/packages/console/core/migrations/meta/0038_snapshot.json new file mode 100644 index 000000000..2463e867c --- /dev/null +++ b/packages/console/core/migrations/meta/0038_snapshot.json @@ -0,0 +1,1037 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "9d5d9885-7ec5-45f6-ac53-45a8e25dede7", + "prevId": "8b7fa839-a088-408e-84a4-1a07325c0290", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "account_id_pk": { + "name": "account_id_pk", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "auth": { + "name": "auth", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "enum('email','github','google')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "provider": { + "name": "provider", + "columns": [ + "provider", + "subject" + ], + "isUnique": true + }, + "account_id": { + "name": "account_id", + "columns": [ + "account_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "auth_id_pk": { + "name": "auth_id_pk", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "billing": { + "name": "billing", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_method_id": { + "name": "payment_method_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_method_type": { + "name": "payment_method_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_method_last4": { + "name": "payment_method_last4", + "type": "varchar(4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "monthly_limit": { + "name": "monthly_limit", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "monthly_usage": { + "name": "monthly_usage", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_monthly_usage_updated": { + "name": "time_monthly_usage_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reload": { + "name": "reload", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reload_trigger": { + "name": "reload_trigger", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reload_amount": { + "name": "reload_amount", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reload_error": { + "name": "reload_error", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_reload_error": { + "name": "time_reload_error", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_reload_locked_till": { + "name": "time_reload_locked_till", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "global_customer_id": { + "name": "global_customer_id", + "columns": [ + "customer_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "billing_workspace_id_id_pk": { + "name": "billing_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "payment": { + "name": "payment", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoice_id": { + "name": "invoice_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_id": { + "name": "payment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_refunded": { + "name": "time_refunded", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "payment_workspace_id_id_pk": { + "name": "payment_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "usage": { + "name": "usage", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "output_tokens": { + "name": "output_tokens", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cache_write_5m_tokens": { + "name": "cache_write_5m_tokens", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cache_write_1h_tokens": { + "name": "cache_write_1h_tokens", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key_id": { + "name": "key_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "usage_workspace_id_id_pk": { + "name": "usage_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "ip": { + "name": "ip", + "columns": { + "ip": { + "name": "ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "usage": { + "name": "usage", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "ip_ip_pk": { + "name": "ip_ip_pk", + "columns": [ + "ip" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "key": { + "name": "key", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_used": { + "name": "time_used", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "global_key": { + "name": "global_key", + "columns": [ + "key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "key_workspace_id_id_pk": { + "name": "key_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "model": { + "name": "model", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "model_workspace_model": { + "name": "model_workspace_model", + "columns": [ + "workspace_id", + "model" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "model_workspace_id_id_pk": { + "name": "model_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "provider": { + "name": "provider", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "workspace_provider": { + "name": "workspace_provider", + "columns": [ + "workspace_id", + "provider" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "provider_workspace_id_id_pk": { + "name": "provider_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_seen": { + "name": "time_seen", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "enum('admin','member')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "monthly_limit": { + "name": "monthly_limit", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "monthly_usage": { + "name": "monthly_usage", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_monthly_usage_updated": { + "name": "time_monthly_usage_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_account_id": { + "name": "user_account_id", + "columns": [ + "workspace_id", + "account_id" + ], + "isUnique": true + }, + "user_email": { + "name": "user_email", + "columns": [ + "workspace_id", + "email" + ], + "isUnique": true + }, + "global_account_id": { + "name": "global_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "global_email": { + "name": "global_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_workspace_id_id_pk": { + "name": "user_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "workspace": { + "name": "workspace", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(now())" + }, + "time_updated": { + "name": "time_updated", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "slug": { + "name": "slug", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "workspace_id": { + "name": "workspace_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/console/core/migrations/meta/_journal.json b/packages/console/core/migrations/meta/_journal.json index 250fe59b3..bb170c51d 100644 --- a/packages/console/core/migrations/meta/_journal.json +++ b/packages/console/core/migrations/meta/_journal.json @@ -267,6 +267,13 @@ "when": 1761928273807, "tag": "0037_messy_jackal", "breakpoints": true + }, + { + "idx": 38, + "version": "5", + "when": 1764110043942, + "tag": "0038_famous_magik", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts index fd6cd095e..8cc181b7c 100644 --- a/packages/console/core/src/model.ts +++ b/packages/console/core/src/model.ts @@ -24,6 +24,12 @@ export namespace ZenData { cost: ModelCostSchema, cost200K: ModelCostSchema.optional(), allowAnonymous: z.boolean().optional(), + trial: z + .object({ + limit: z.number(), + provider: z.string(), + }) + .optional(), rateLimit: z.number().optional(), fallbackProvider: z.string().optional(), providers: z.array( diff --git a/packages/console/core/src/schema/ip.sql.ts b/packages/console/core/src/schema/ip.sql.ts new file mode 100644 index 000000000..be5fb7fa2 --- /dev/null +++ b/packages/console/core/src/schema/ip.sql.ts @@ -0,0 +1,12 @@ +import { mysqlTable, int, primaryKey, varchar } from "drizzle-orm/mysql-core" +import { timestamps } from "../drizzle/types" + +export const IpTable = mysqlTable( + "ip", + { + ip: varchar("ip", { length: 45 }).notNull(), + ...timestamps, + usage: int("usage"), + }, + (table) => [primaryKey({ columns: [table.ip] })], +) From c2fc41dcd5bd8a8165780dfe3f53c2123fa8a45d Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 25 Nov 2025 17:57:22 -0500 Subject: [PATCH 17/35] wip: zen --- packages/sdk/python/sst.pyi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/sdk/python/sst.pyi b/packages/sdk/python/sst.pyi index 1c423e9ac..984fecde5 100644 --- a/packages/sdk/python/sst.pyi +++ b/packages/sdk/python/sst.pyi @@ -55,6 +55,9 @@ class Resource: class EMAILOCTOPUS_API_KEY: type: str value: str + class Enterprise: + type: str + url: str class EnterpriseStorage: name: str type: str From a1e87f6cd99f5f0b7d6ca7fbdeb12ccd51c1709d Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 25 Nov 2025 17:58:59 -0500 Subject: [PATCH 18/35] wip: zen --- .../src/routes/zen/util/provider/provider.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/console/app/src/routes/zen/util/provider/provider.ts b/packages/console/app/src/routes/zen/util/provider/provider.ts index 8366f3a63..730ad5a27 100644 --- a/packages/console/app/src/routes/zen/util/provider/provider.ts +++ b/packages/console/app/src/routes/zen/util/provider/provider.ts @@ -24,6 +24,15 @@ import { toOaCompatibleResponse, } from "./openai-compatible" +export type UsageInfo = { + inputTokens: number + outputTokens: number + reasoningTokens?: number + cacheReadTokens?: number + cacheWrite5mTokens?: number + cacheWrite1hTokens?: number +} + export type ProviderHelper = { format: ZenData.Format modifyUrl: (providerApi: string, model?: string, isStream?: boolean) => string @@ -34,14 +43,7 @@ export type ProviderHelper = { parse: (chunk: string) => void retrieve: () => any } - normalizeUsage: (usage: any) => { - inputTokens: number - outputTokens: number - reasoningTokens?: number - cacheReadTokens?: number - cacheWrite5mTokens?: number - cacheWrite1hTokens?: number - } + normalizeUsage: (usage: any) => UsageInfo } export interface CommonMessage { From 52f97ffdc9d151b8f11e56217dc1b770eac270e0 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 25 Nov 2025 22:59:55 +0000 Subject: [PATCH 19/35] chore: format code --- .../core/migrations/meta/0038_snapshot.json | 102 ++++-------------- .../core/migrations/meta/_journal.json | 2 +- 2 files changed, 24 insertions(+), 80 deletions(-) diff --git a/packages/console/core/migrations/meta/0038_snapshot.json b/packages/console/core/migrations/meta/0038_snapshot.json index 2463e867c..b0a59c497 100644 --- a/packages/console/core/migrations/meta/0038_snapshot.json +++ b/packages/console/core/migrations/meta/0038_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -277,9 +268,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -287,10 +276,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -377,10 +363,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -502,10 +485,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -557,9 +537,7 @@ "compositePrimaryKeys": { "ip_ip_pk": { "name": "ip_ip_pk", - "columns": [ - "ip" - ] + "columns": ["ip"] } }, "uniqueConstraints": {}, @@ -637,9 +615,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -647,10 +623,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -707,10 +680,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -718,10 +688,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -785,10 +752,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -796,10 +760,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -912,32 +873,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -945,10 +896,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -1005,9 +953,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -1015,9 +961,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -1034,4 +978,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/_journal.json b/packages/console/core/migrations/meta/_journal.json index bb170c51d..8a1a38551 100644 --- a/packages/console/core/migrations/meta/_journal.json +++ b/packages/console/core/migrations/meta/_journal.json @@ -276,4 +276,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} From 14a910bd64ae3c11b8ff0d2a6a5789d46bccb008 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 25 Nov 2025 18:04:23 -0500 Subject: [PATCH 20/35] wip: zen --- packages/console/app/src/routes/zen/util/trialLimiter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/console/app/src/routes/zen/util/trialLimiter.ts b/packages/console/app/src/routes/zen/util/trialLimiter.ts index 82debbb73..15561c9f6 100644 --- a/packages/console/app/src/routes/zen/util/trialLimiter.ts +++ b/packages/console/app/src/routes/zen/util/trialLimiter.ts @@ -4,6 +4,7 @@ import { UsageInfo } from "./provider/provider" export function createTrialLimiter(limit: number | undefined, ip: string) { if (!limit) return + if (!ip) return let trial: boolean From 69c2dd53adf126a3c68d2974b8753ae0f92ee854 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 25 Nov 2025 23:53:56 +0000 Subject: [PATCH 21/35] config: add setCacheKey in provider options (#4738) Co-authored-by: GitHub Action Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/config/config.ts | 4 ++++ packages/opencode/src/provider/transform.ts | 10 ++++++++-- packages/opencode/src/session/prompt.ts | 18 +++++++++++++++--- packages/sdk/js/src/gen/types.gen.ts | 6 +++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 28b8ca3b2..0ea0e8fa2 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -540,6 +540,10 @@ export namespace Config { apiKey: z.string().optional(), baseURL: z.string().optional(), enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"), + setCacheKey: z + .boolean() + .optional() + .describe("Enable promptCacheKey for this provider (default false)"), timeout: z .union([ z diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 6cf03fc06..2b9c53bbf 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -128,7 +128,13 @@ export namespace ProviderTransform { return undefined } - export function options(providerID: string, modelID: string, npm: string, sessionID: string): Record { + export function options( + providerID: string, + modelID: string, + npm: string, + sessionID: string, + providerOptions?: Record, + ): Record { const result: Record = {} // switch to providerID later, for now use this @@ -138,7 +144,7 @@ export namespace ProviderTransform { } } - if (providerID === "openai") { + if (providerID === "openai" || providerOptions?.setCacheKey) { result["promptCacheKey"] = sessionID } diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index e6c64f96b..c6721202a 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -477,13 +477,14 @@ export namespace SessionPrompt { tools: lastUser.tools, processor, }) + const provider = await Provider.getProvider(model.providerID) const params = await Plugin.trigger( "chat.params", { sessionID: sessionID, agent: lastUser.agent, model: model.info, - provider: await Provider.getProvider(model.providerID), + provider, message: lastUser, }, { @@ -493,7 +494,9 @@ export namespace SessionPrompt { topP: agent.topP ?? ProviderTransform.topP(model.providerID, model.modelID), options: pipe( {}, - mergeDeep(ProviderTransform.options(model.providerID, model.modelID, model.npm ?? "", sessionID)), + mergeDeep( + ProviderTransform.options(model.providerID, model.modelID, model.npm ?? "", sessionID, provider?.options), + ), mergeDeep(model.info.options), mergeDeep(agent.options), ), @@ -1412,9 +1415,18 @@ export namespace SessionPrompt { if (!isFirst) return const small = (await Provider.getSmallModel(input.providerID)) ?? (await Provider.getModel(input.providerID, input.modelID)) + const provider = await Provider.getProvider(small.providerID) const options = pipe( {}, - mergeDeep(ProviderTransform.options(small.providerID, small.modelID, small.npm ?? "", input.session.id)), + mergeDeep( + ProviderTransform.options( + small.providerID, + small.modelID, + small.npm ?? "", + input.session.id, + provider?.options, + ), + ), mergeDeep(ProviderTransform.smallOptions({ providerID: small.providerID, modelID: small.modelID })), mergeDeep(small.info.options), ) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 2de8ca2f1..6e3b8c071 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1123,11 +1123,15 @@ export type Config = { * GitHub Enterprise URL for copilot authentication */ enterpriseUrl?: string + /** + * Enable promptCacheKey for this provider (default false) + */ + setCacheKey?: boolean /** * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout. */ timeout?: number | false - [key: string]: unknown | string | (number | false) | undefined + [key: string]: unknown | string | boolean | (number | false) | undefined } } } From e8db95be16ce25807bf6f218cac0879cdca1ee4d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 25 Nov 2025 19:14:07 -0500 Subject: [PATCH 22/35] switch typescript lsp to be one per package to ensure it loads when typescript is not installed at root --- packages/opencode/src/lsp/server.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 79a2a408b..af96a02d8 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -82,13 +82,10 @@ export namespace LSPServer { export const Typescript: Info = { id: "typescript", - root: NearestRoot( - ["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"], - ["deno.json", "deno.jsonc"], - ), + root: NearestRoot(["tsconfig.json", "package.json", "jsconfig.json", ".git"], ["deno.json", "deno.jsonc"]), extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], async spawn(root) { - const tsserver = await Bun.resolve("typescript/lib/tsserver.js", Instance.directory).catch(() => {}) + const tsserver = await Bun.resolve("typescript/lib/tsserver.js", root).catch(() => {}) if (!tsserver) return const proc = spawn(BunProc.which(), ["x", "typescript-language-server", "--stdio"], { cwd: root, From 0ff4c284e2dcd9006ef4e8d7878abc9e723c2526 Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 26 Nov 2025 00:21:59 +0000 Subject: [PATCH 23/35] release: v1.0.113 --- 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 b81a80cfe..74e86e3af 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -47,7 +47,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -74,7 +74,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -98,7 +98,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -122,7 +122,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -163,7 +163,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -191,7 +191,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -207,7 +207,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.112", + "version": "1.0.113", "bin": { "opencode": "./bin/opencode", }, @@ -294,7 +294,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -314,7 +314,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.112", + "version": "1.0.113", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -325,7 +325,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -338,7 +338,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", @@ -351,7 +351,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -383,7 +383,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.112", + "version": "1.0.113", "dependencies": { "zod": "catalog:", }, @@ -393,7 +393,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.112", + "version": "1.0.113", "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 d86cc873a..779c2cfca 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.112", + "version": "1.0.113", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 61968d1cf..70b229684 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.112", + "version": "1.0.113", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index ec5a0371c..85150564b 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.112", + "version": "1.0.113", "$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 89eedaeef..915e7f92f 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.112", + "version": "1.0.113", "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 fb51dc1e1..09496f930 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.112", + "version": "1.0.113", "description": "", "type": "module", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 3199f75d2..4929a91b3 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.112", + "version": "1.0.113", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 7bdcfcc75..b6eb199dd 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.112" +version = "1.0.113" 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.112/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.113/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.112/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.113/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.112/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.113/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.112/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.113/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.112/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.113/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 93ae9e630..54a7c71e2 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.112", + "version": "1.0.113", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index aabc98ae4..e72151163 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.112", + "version": "1.0.113", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index e7f3b4064..c5b821ed9 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.112", + "version": "1.0.113", "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 1173d9dfa..d06cfba88 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.112", + "version": "1.0.113", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index f0e879331..a308e07ab 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.112", + "version": "1.0.113", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index 8191fb8b4..31ef42231 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.112", + "version": "1.0.113", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/ui/package.json b/packages/ui/package.json index e8ae9ea7c..7f107631c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.112", + "version": "1.0.113", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index a573fac3c..294c2f324 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.112", + "version": "1.0.113", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index c799d6410..0ddc86c3b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.112", + "version": "1.0.113", "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 5d206a627..e4460b818 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.112", + "version": "1.0.113", "publisher": "sst-dev", "repository": { "type": "git", From b44971668cf023ffc6a36deeb26bd177e9638004 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:16:13 -0800 Subject: [PATCH 24/35] fix: global prefix handling w/ aws bedrock (#4757) --- .github/workflows/snapshot.yml | 2 +- packages/opencode/src/provider/provider.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 815433f03..f8bbb78c6 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -4,7 +4,7 @@ on: push: branches: - dev - - fix-snapshot-2 + - test-bedrock - v0 concurrency: ${{ github.workflow }}-${{ github.ref }} diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 1cf0312ea..a4c406c0f 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -130,6 +130,11 @@ export namespace Provider { credentialProvider: fromNodeProviderChain(), }, async getModel(sdk: any, modelID: string, _options?: Record) { + // Skip region prefixing if model already has global prefix + if (modelID.startsWith("global.")) { + return sdk.languageModel(modelID) + } + let regionPrefix = region.split("-")[0] switch (regionPrefix) { From b16aa81e0d2c09819ceb1e6fa035356a2ffa2347 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 25 Nov 2025 19:22:49 -0500 Subject: [PATCH 25/35] switch to vtsls for typescript lsp --- packages/opencode/src/lsp/server.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index af96a02d8..44752fd50 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -82,12 +82,13 @@ export namespace LSPServer { export const Typescript: Info = { id: "typescript", - root: NearestRoot(["tsconfig.json", "package.json", "jsconfig.json", ".git"], ["deno.json", "deno.jsonc"]), + root: NearestRoot( + ["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"], + ["deno.json", "deno.jsonc"], + ), extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], async spawn(root) { - const tsserver = await Bun.resolve("typescript/lib/tsserver.js", root).catch(() => {}) - if (!tsserver) return - const proc = spawn(BunProc.which(), ["x", "typescript-language-server", "--stdio"], { + const proc = spawn(BunProc.which(), ["x", "@vtsls/language-server", "--stdio"], { cwd: root, env: { ...process.env, @@ -96,11 +97,6 @@ export namespace LSPServer { }) return { process: proc, - initialization: { - tsserver: { - path: tsserver, - }, - }, } }, } From ced5fdbe70370cfe92b65c6f7ac924cc63466c05 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 26 Nov 2025 00:24:10 +0000 Subject: [PATCH 26/35] chore: format code --- 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 c5b821ed9..b0560239e 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 d06cfba88..d01cbced9 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 4114c8715c65bf55c7c1d81daf87b6d3639968e3 Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 26 Nov 2025 00:32:22 +0000 Subject: [PATCH 27/35] release: v1.0.114 --- 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 74e86e3af..d10b145c5 100644 --- a/bun.lock +++ b/bun.lock @@ -19,7 +19,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -47,7 +47,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -74,7 +74,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -98,7 +98,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -122,7 +122,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -163,7 +163,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -191,7 +191,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -207,7 +207,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.113", + "version": "1.0.114", "bin": { "opencode": "./bin/opencode", }, @@ -294,7 +294,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -314,7 +314,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.113", + "version": "1.0.114", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -325,7 +325,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -338,7 +338,7 @@ }, "packages/tauri": { "name": "@opencode-ai/tauri", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", @@ -351,7 +351,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -383,7 +383,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.113", + "version": "1.0.114", "dependencies": { "zod": "catalog:", }, @@ -393,7 +393,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.113", + "version": "1.0.114", "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 779c2cfca..76b333dff 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.113", + "version": "1.0.114", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 70b229684..ad2dcdf4a 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.113", + "version": "1.0.114", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 85150564b..f0e09c25c 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.113", + "version": "1.0.114", "$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 915e7f92f..de270f8ff 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.113", + "version": "1.0.114", "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 09496f930..a98738c4c 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.113", + "version": "1.0.114", "description": "", "type": "module", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 4929a91b3..965e58db5 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.113", + "version": "1.0.114", "private": true, "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index b6eb199dd..2dc3c7a1b 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.113" +version = "1.0.114" 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.113/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.114/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.113/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.114/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.113/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.114/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.113/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.114/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.113/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.114/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 54a7c71e2..2868abcd3 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.113", + "version": "1.0.114", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e72151163..e75542083 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.113", + "version": "1.0.114", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index b0560239e..69bc63770 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.113", + "version": "1.0.114", "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 d01cbced9..561202aba 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.113", + "version": "1.0.114", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index a308e07ab..8f6af596f 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.113", + "version": "1.0.114", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/package.json b/packages/tauri/package.json index 31ef42231..9363c50b4 100644 --- a/packages/tauri/package.json +++ b/packages/tauri/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/tauri", "private": true, - "version": "1.0.113", + "version": "1.0.114", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/ui/package.json b/packages/ui/package.json index 7f107631c..2c06c6da0 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.113", + "version": "1.0.114", "type": "module", "exports": { "./*": "./src/components/*.tsx", diff --git a/packages/util/package.json b/packages/util/package.json index 294c2f324..d99bdae17 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.113", + "version": "1.0.114", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 0ddc86c3b..95c4850ad 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.113", + "version": "1.0.114", "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 e4460b818..35506a804 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.113", + "version": "1.0.114", "publisher": "sst-dev", "repository": { "type": "git", From 50c40a8d99bb4bcce4b978eaf9a9bd38101336fa Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 25 Nov 2025 21:32:56 -0500 Subject: [PATCH 28/35] tui: fix event subscription cleanup in SDK context --- .../opencode/src/cli/cmd/tui/context/sdk.tsx | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx index fa3b1c633..401a53ab4 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx @@ -1,7 +1,8 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk" import { createSimpleContext } from "./helper" import { createGlobalEmitter } from "@solid-primitives/event-bus" -import { batch, onCleanup } from "solid-js" +import { batch, onCleanup, onMount } from "solid-js" +import { iife } from "@/util/iife" export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", @@ -16,43 +17,49 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ [key in Event["type"]]: Extract }>() - sdk.event.subscribe().then(async (events) => { - let queue: Event[] = [] - let timer: Timer | undefined - let last = 0 - - const flush = () => { - if (queue.length === 0) return - const events = queue - queue = [] - timer = undefined - last = Date.now() - // Batch all event emissions so all store updates result in a single render - batch(() => { - for (const event of events) { - emitter.emit(event.type, event) - } + onMount(async () => { + while (true) { + if (abort.signal.aborted) break + const events = await sdk.event.subscribe({ + signal: abort.signal, }) - } + let queue: Event[] = [] + let timer: Timer | undefined + let last = 0 - for await (const event of events.stream) { - queue.push(event) - const elapsed = Date.now() - last - - if (timer) continue - // If we just flushed recently (within 16ms), batch this with future events - // Otherwise, process immediately to avoid latency - if (elapsed < 16) { - timer = setTimeout(flush, 16) - continue + const flush = () => { + if (queue.length === 0) return + const events = queue + queue = [] + timer = undefined + last = Date.now() + // Batch all event emissions so all store updates result in a single render + batch(() => { + for (const event of events) { + emitter.emit(event.type, event) + } + }) } - flush() - } - // Flush any remaining events - if (timer) clearTimeout(timer) - if (queue.length > 0) { - flush() + for await (const event of events.stream) { + queue.push(event) + const elapsed = Date.now() - last + + if (timer) continue + // If we just flushed recently (within 16ms), batch this with future events + // Otherwise, process immediately to avoid latency + if (elapsed < 16) { + timer = setTimeout(flush, 16) + continue + } + flush() + } + + // Flush any remaining events + if (timer) clearTimeout(timer) + if (queue.length > 0) { + flush() + } } }) From 16a188c5248ebfcd315b1b473b7524a0ffd37678 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 26 Nov 2025 02:33:36 +0000 Subject: [PATCH 29/35] chore: format code --- 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 69bc63770..c08186b20 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 561202aba..1d8ae6371 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From ef441d5cffb279727d9dfa17693f0effe30c22f2 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 25 Nov 2025 19:59:37 -0600 Subject: [PATCH 30/35] chore: cleanup pierre stuff --- .../enterprise/src/routes/share/[shareID].tsx | 16 +--- packages/ui/package.json | 1 + packages/ui/src/components/code.tsx | 19 +---- packages/ui/src/components/diff.tsx | 76 +++++-------------- packages/ui/src/components/pierre.ts | 44 +++++++++++ 5 files changed, 67 insertions(+), 89 deletions(-) create mode 100644 packages/ui/src/components/pierre.ts diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 42dd7e957..fd6638a5f 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -7,6 +7,7 @@ import { createEffect, createMemo, ErrorBoundary, For, Match, Show, Switch } fro import { Share } from "~/core/share" import { Logo, Mark } from "@opencode-ai/ui/logo" import { IconButton } from "@opencode-ai/ui/icon-button" +import { createDefaultOptions } from "@opencode-ai/ui/pierre" import { iife } from "@opencode-ai/util/iife" import { Binary } from "@opencode-ai/util/binary" import { NamedError } from "@opencode-ai/util/error" @@ -82,20 +83,7 @@ const getData = query(async (shareID) => { preloadMultiFileDiff({ oldFile: { name: diff.file, contents: diff.before }, newFile: { name: diff.file, contents: diff.after }, - options: { - theme: "OpenCode", - themeType: "system", - disableLineNumbers: false, - overflow: "wrap", - diffStyle: "unified", - diffIndicators: "bars", - disableBackground: false, - expansionLineCount: 20, - lineDiffType: "none", - maxLineDiffLength: 1000, - maxLineLengthForHighlighting: 1000, - disableFileHeader: true, - }, + options: createDefaultOptions("unified"), // annotations, }), ), diff --git a/packages/ui/package.json b/packages/ui/package.json index 2c06c6da0..eb126a6fa 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -4,6 +4,7 @@ "type": "module", "exports": { "./*": "./src/components/*.tsx", + "./pierre": "./src/components/pierre.ts", "./hooks": "./src/hooks/index.ts", "./context": "./src/context/index.ts", "./context/*": "./src/context/*.tsx", diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx index a301e34c7..788baf549 100644 --- a/packages/ui/src/components/code.tsx +++ b/packages/ui/src/components/code.tsx @@ -1,5 +1,6 @@ import { type FileContents, File, FileOptions, LineAnnotation } from "@pierre/precision-diffs" import { ComponentProps, createEffect, splitProps } from "solid-js" +import { createDefaultOptions, styleVariables } from "./pierre" export type CodeProps = FileOptions & { file: FileContents @@ -14,12 +15,7 @@ export function Code(props: CodeProps) { createEffect(() => { const instance = new File({ - theme: "OpenCode", - overflow: "wrap", // or 'scroll' - themeType: "system", // 'system', 'light', or 'dark' - disableFileHeader: true, - disableLineNumbers: false, // optional - // lang: 'typescript', // optional - auto-detected from filename if not provided + ...createDefaultOptions("unified"), ...others, }) @@ -34,16 +30,7 @@ export function Code(props: CodeProps) { return (
= FileDiffOptions & { preloadedDiff?: PreloadMultiFileDiffResult @@ -15,6 +16,8 @@ export type DiffProps = FileDiffOptions & { // interface ThreadMetadata { // threadId: string // } +// +// export function Diff(props: DiffProps) { let container!: HTMLDivElement @@ -24,27 +27,12 @@ export function Diff(props: DiffProps) { let fileDiffInstance: FileDiff | undefined const cleanupFunctions: Array<() => void> = [] - const defaultOptions: FileDiffOptions = { - theme: "OpenCode", - themeType: "system", - disableLineNumbers: false, - overflow: "wrap", - diffStyle: "unified", - diffIndicators: "bars", - disableBackground: false, - expansionLineCount: 20, - lineDiffType: props.diffStyle === "split" ? "word-alt" : "none", - maxLineDiffLength: 1000, - maxLineLengthForHighlighting: 1000, - disableFileHeader: true, - } - createEffect(() => { if (props.preloadedDiff) return container.innerHTML = "" if (!fileDiffInstance) { fileDiffInstance = new FileDiff({ - ...defaultOptions, + ...createDefaultOptions(props.diffStyle), ...others, ...(props.preloadedDiff ?? {}), }) @@ -60,22 +48,19 @@ export function Diff(props: DiffProps) { onMount(() => { if (isServer) return fileDiffInstance = new FileDiff({ - ...defaultOptions, - // You can optionally pass a render function for rendering out line - // annotations. Just return the dom node to render - // renderAnnotation(annotation: DiffLineAnnotation): HTMLElement { - // // Despite the diff itself being rendered in the shadow dom, - // // annotations are inserted via the web components 'slots' api and you - // // can use all your normal normal css and styling for them - // const element = document.createElement("div") - // element.innerText = annotation.metadata.threadId - // return element - // }, + ...createDefaultOptions(props.diffStyle), ...others, ...(props.preloadedDiff ?? {}), }) // @ts-expect-error - fileContainer is private but needed for SSR hydration fileDiffInstance.fileContainer = fileDiffRef + fileDiffInstance.hydrate({ + oldFile: local.before, + newFile: local.after, + lineAnnotations: local.annotations, + fileContainer: fileDiffRef, + containerWrapper: container, + }) // Hydrate annotation slots with interactive SolidJS components // if (props.annotations.length > 0 && props.renderAnnotation != null) { @@ -108,38 +93,11 @@ export function Diff(props: DiffProps) { }) return ( -
+
- {/* Only render on server - client hydrates the existing content */} - {isServer && props.preloadedDiff && ( - <> - {/* Declarative Shadow DOM - browsers parse this and create a shadow root */} - - {/* Render static annotation slots on server. - Client will clear these and mount interactive components. */} - {/* */} - {/* {(annotation) => { */} - {/* const slotName = `annotation-${annotation.side}-${annotation.lineNumber}` */} - {/* return
{props.renderAnnotation?.(annotation)}
*/} - {/* }} */} - {/*
*/} - - )} + + {(preloadedDiff) =>