From 7cab6824d1bf83f147c8a86b5cb5ca499c8eda13 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 25 Apr 2026 21:54:58 -0400 Subject: [PATCH 01/34] feat(httpapi): bridge experimental tool routes (#24407) --- packages/opencode/specs/effect/http-api.md | 8 +-- .../routes/instance/httpapi/experimental.ts | 70 ++++++++++++++++++- .../src/server/routes/instance/index.ts | 2 + .../test/server/httpapi-experimental.test.ts | 35 +++++++++- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 2deec51a22..c09d7c8ed2 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -181,7 +181,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho | `mcp` | `bridged` | status, add, OAuth, connect/disconnect | | `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | | top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` partial | console reads, tool ids, worktree list/mutations, resource list; global session list remains later | +| experimental JSON routes | `bridged` partial | console, tool, worktree list/mutations, resource list; global session list remains later | | `session` | `later/special` | large stateful surface plus streaming | | `sync` | `later` | process/control side effects | | `event` | `special` | SSE | @@ -259,9 +259,9 @@ This checklist tracks bridge parity only. Checked routes are available through t - [x] `GET /experimental/console` - active Console provider metadata. - [x] `GET /experimental/console/orgs` - switchable Console orgs. -- [ ] `POST /experimental/console/switch` - switch active Console org. +- [x] `POST /experimental/console/switch` - switch active Console org. - [x] `GET /experimental/tool/ids` - tool IDs. -- [ ] `GET /experimental/tool` - tools for provider/model. +- [x] `GET /experimental/tool` - tools for provider/model. - [x] `GET /experimental/worktree` - list worktrees. - [x] `POST /experimental/worktree` - create worktree. - [x] `DELETE /experimental/worktree` - remove worktree. @@ -350,7 +350,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 1. [x] Bridge `PATCH /project/:projectID`. 2. [x] Bridge MCP add/connect/disconnect routes. 3. [x] Bridge MCP OAuth routes: start, callback, authenticate, remove. -4. [ ] Bridge experimental console switch and tool list routes. +4. [x] Bridge experimental console switch and tool list routes. 5. [ ] Bridge experimental global session list. 6. [ ] Bridge workspace create/remove/session-restore routes. 7. [ ] Bridge sync start/replay/history routes. diff --git a/packages/opencode/src/server/routes/instance/httpapi/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/experimental.ts index 14f54d457a..392302d429 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/experimental.ts @@ -1,12 +1,16 @@ import { Account } from "@/account/account" +import { AccountID, OrgID } from "@/account/schema" +import { Agent } from "@/agent/agent" import { Config } from "@/config" import { InstanceState } from "@/effect" import { MCP } from "@/mcp" import { Project } from "@/project" +import { ProviderID, ModelID } from "@/provider/schema" import { ToolRegistry } from "@/tool" +import * as EffectZod from "@/util/effect-zod" import { Worktree } from "@/worktree" import { Effect, Layer, Option, Schema } from "effect" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "./auth" const ConsoleStateResponse = Schema.Struct({ @@ -28,13 +32,30 @@ const ConsoleOrgList = Schema.Struct({ orgs: Schema.Array(ConsoleOrgOption), }).annotate({ identifier: "ConsoleOrgList" }) +const ConsoleSwitchPayload = Schema.Struct({ + accountID: AccountID, + orgID: OrgID, +}).annotate({ identifier: "ConsoleSwitchInput" }) + const ToolIDs = Schema.Array(Schema.String).annotate({ identifier: "ToolIDs" }) +const ToolListItem = Schema.Struct({ + id: Schema.String, + description: Schema.String, + parameters: Schema.Record(Schema.String, Schema.Any), +}).annotate({ identifier: "ToolListItem" }) +const ToolList = Schema.Array(ToolListItem).annotate({ identifier: "ToolList" }) +const ToolListQuery = Schema.Struct({ + provider: ProviderID, + model: ModelID, +}) const WorktreeList = Schema.Array(Schema.String).annotate({ identifier: "WorktreeList" }) export const ExperimentalPaths = { console: "/experimental/console", consoleOrgs: "/experimental/console/orgs", + consoleSwitch: "/experimental/console/switch", + tool: "/experimental/tool", toolIDs: "/experimental/tool/ids", worktree: "/experimental/worktree", worktreeReset: "/experimental/worktree/reset", @@ -63,6 +84,27 @@ export const ExperimentalApi = HttpApi.make("experimental") description: "Get the available Console orgs across logged-in accounts, including the current active org.", }), ), + HttpApiEndpoint.post("consoleSwitch", ExperimentalPaths.consoleSwitch, { + payload: ConsoleSwitchPayload, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "experimental.console.switchOrg", + summary: "Switch active Console org", + description: "Persist a new active Console account/org selection for the current local OpenCode state.", + }), + ), + HttpApiEndpoint.get("tool", ExperimentalPaths.tool, { + query: ToolListQuery, + success: ToolList, + }).annotateMerge( + OpenApi.annotations({ + identifier: "tool.list", + summary: "List tools", + description: + "Get a list of available tools with their JSON schema parameters for a specific provider and model combination.", + }), + ), HttpApiEndpoint.get("toolIDs", ExperimentalPaths.toolIDs, { success: ToolIDs, }).annotateMerge( @@ -141,6 +183,7 @@ export const ExperimentalApi = HttpApi.make("experimental") export const experimentalHandlers = Layer.unwrap( Effect.gen(function* () { const account = yield* Account.Service + const agents = yield* Agent.Service const config = yield* Config.Service const mcp = yield* MCP.Service const project = yield* Project.Service @@ -183,6 +226,28 @@ export const experimentalHandlers = Layer.unwrap( } }) + const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: { + payload: typeof ConsoleSwitchPayload.Type + }) { + yield* account + .use(ctx.payload.accountID, Option.some(ctx.payload.orgID)) + .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({})))) + return true + }) + + const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) { + const list = yield* registry.tools({ + providerID: ctx.query.provider, + modelID: ctx.query.model, + agent: yield* agents.get(yield* agents.defaultAgent()), + }) + return list.map((item) => ({ + id: item.id, + description: item.description, + parameters: EffectZod.toJsonSchema(item.parameters), + })) + }) + const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () { return yield* registry.ids() }) @@ -222,6 +287,8 @@ export const experimentalHandlers = Layer.unwrap( handlers .handle("console", getConsole) .handle("consoleOrgs", listConsoleOrgs) + .handle("consoleSwitch", switchConsole) + .handle("tool", tool) .handle("toolIDs", toolIDs) .handle("worktree", worktree) .handle("worktreeCreate", worktreeCreate) @@ -232,6 +299,7 @@ export const experimentalHandlers = Layer.unwrap( }), ).pipe( Layer.provide(Account.defaultLayer), + Layer.provide(Agent.defaultLayer), Layer.provide(Config.defaultLayer), Layer.provide(MCP.defaultLayer), Layer.provide(Project.defaultLayer), diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index ad686ba08c..b99aa948e8 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -49,6 +49,8 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.get("/config/providers", (c) => handler(c.req.raw, context)) app.get(ExperimentalPaths.console, (c) => handler(c.req.raw, context)) app.get(ExperimentalPaths.consoleOrgs, (c) => handler(c.req.raw, context)) + app.post(ExperimentalPaths.consoleSwitch, (c) => handler(c.req.raw, context)) + app.get(ExperimentalPaths.tool, (c) => handler(c.req.raw, context)) app.get(ExperimentalPaths.toolIDs, (c) => handler(c.req.raw, context)) app.get(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context)) app.post(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context)) diff --git a/packages/opencode/test/server/httpapi-experimental.test.ts b/packages/opencode/test/server/httpapi-experimental.test.ts index e704750eaf..84f8cef65f 100644 --- a/packages/opencode/test/server/httpapi-experimental.test.ts +++ b/packages/opencode/test/server/httpapi-experimental.test.ts @@ -5,6 +5,7 @@ import { GlobalBus } from "@/bus/global" import { Instance } from "../../src/project/instance" import { InstanceRoutes } from "../../src/server/routes/instance" import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/experimental" +import { Database } from "../../src/storage" import { Log } from "../../src/util" import { Worktree } from "../../src/worktree" import { resetDatabase } from "../fixture/db" @@ -14,6 +15,7 @@ void Log.init({ print: false }) const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket +const testWorktreeMutations = process.platform === "win32" ? test.skip : test function app() { Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true @@ -61,9 +63,10 @@ describe("experimental HttpApi", () => { }) const headers = { "x-opencode-directory": tmp.path } - const [consoleState, consoleOrgs, toolIDs, worktrees, resources] = await Promise.all([ + const [consoleState, consoleOrgs, toolList, toolIDs, worktrees, resources] = await Promise.all([ app().request(ExperimentalPaths.console, { headers }), app().request(ExperimentalPaths.consoleOrgs, { headers }), + app().request(`${ExperimentalPaths.tool}?provider=opencode&model=gpt-5`, { headers }), app().request(ExperimentalPaths.toolIDs, { headers }), app().request(ExperimentalPaths.worktree, { headers }), app().request(ExperimentalPaths.resource, { headers }), @@ -78,6 +81,15 @@ describe("experimental HttpApi", () => { expect(consoleOrgs.status).toBe(200) expect(await consoleOrgs.json()).toEqual({ orgs: [] }) + expect(toolList.status).toBe(200) + expect(await toolList.json()).toContainEqual( + expect.objectContaining({ + id: "bash", + description: expect.any(String), + parameters: expect.any(Object), + }), + ) + expect(toolIDs.status).toBe(200) expect(await toolIDs.json()).toContain("bash") @@ -88,7 +100,26 @@ describe("experimental HttpApi", () => { expect(await resources.json()).toEqual({}) }) - test("serves worktree mutations through Hono bridge", async () => { + test("serves Console org switch through Hono bridge", async () => { + await using tmp = await tmpdir({ config: { formatter: false, lsp: false } }) + Database.Client() + .$client + .prepare( + "INSERT INTO account (id, email, url, access_token, refresh_token, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", + ) + .run("account-test", "test@example.com", "https://console.example.com", "access", "refresh", Date.now(), Date.now()) + + const switched = await app().request(ExperimentalPaths.consoleSwitch, { + method: "POST", + headers: { "x-opencode-directory": tmp.path, "content-type": "application/json" }, + body: JSON.stringify({ accountID: "account-test", orgID: "org-test" }), + }) + + expect(switched.status).toBe(200) + expect(await switched.json()).toBe(true) + }) + + testWorktreeMutations("serves worktree mutations through Hono bridge", async () => { await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } From 097d9306689a9b79a7f8484a00d8d7b91fcda91b Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 01:55:57 +0000 Subject: [PATCH 02/34] chore: generate --- packages/opencode/specs/effect/http-api.md | 34 +++++++++---------- .../test/server/httpapi-experimental.test.ts | 13 +++++-- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index c09d7c8ed2..a336804600 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | -------------------------------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` partial | console, tool, worktree list/mutations, resource list; global session list remains later | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `later` | process/control side effects | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` partial | console, tool, worktree list/mutations, resource list; global session list remains later | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `later` | process/control side effects | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist diff --git a/packages/opencode/test/server/httpapi-experimental.test.ts b/packages/opencode/test/server/httpapi-experimental.test.ts index 84f8cef65f..cf673dc969 100644 --- a/packages/opencode/test/server/httpapi-experimental.test.ts +++ b/packages/opencode/test/server/httpapi-experimental.test.ts @@ -103,11 +103,18 @@ describe("experimental HttpApi", () => { test("serves Console org switch through Hono bridge", async () => { await using tmp = await tmpdir({ config: { formatter: false, lsp: false } }) Database.Client() - .$client - .prepare( + .$client.prepare( "INSERT INTO account (id, email, url, access_token, refresh_token, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", ) - .run("account-test", "test@example.com", "https://console.example.com", "access", "refresh", Date.now(), Date.now()) + .run( + "account-test", + "test@example.com", + "https://console.example.com", + "access", + "refresh", + Date.now(), + Date.now(), + ) const switched = await app().request(ExperimentalPaths.consoleSwitch, { method: "POST", From f2d4d816fb488f98f7b9054ec8b6a9c42b93cba7 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 25 Apr 2026 22:11:25 -0400 Subject: [PATCH 03/34] test(provider): avoid plugin dependency install timeout (#24416) --- packages/opencode/test/provider/provider.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 612fe3e97c..da98496c30 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -55,6 +55,14 @@ async function defaultModel() { return run((provider) => provider.defaultModel()) } +async function markPluginDependenciesReady(dir: string) { + await mkdir(path.join(dir, "node_modules"), { recursive: true }) + await Bun.write( + path.join(dir, "package-lock.json"), + JSON.stringify({ packages: { "": { dependencies: { "@opencode-ai/plugin": "0.0.0" } } } }), + ) +} + function paid(providers: Awaited>) { const item = providers[ProviderID.make("opencode")] expect(item).toBeDefined() @@ -2439,8 +2447,11 @@ test("cloudflare-ai-gateway forwards config metadata options", async () => { test("plugin config providers persist after instance dispose", async () => { await using tmp = await tmpdir({ init: async (dir) => { - const root = path.join(dir, ".opencode", "plugin") + const configDir = path.join(dir, ".opencode") + const root = path.join(configDir, "plugin") await mkdir(root, { recursive: true }) + await markPluginDependenciesReady(configDir) + await markPluginDependenciesReady(Global.Path.config) await Bun.write( path.join(root, "demo-provider.ts"), [ From 28935880169fc55eb6114402e3976b2a70f83ace Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 25 Apr 2026 23:23:48 -0400 Subject: [PATCH 04/34] sync --- packages/console/app/src/routes/zen/util/handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index 87d4a4d0ab..c15197b6e3 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -101,12 +101,13 @@ export async function handler( const requestId = input.request.headers.get("x-opencode-request") ?? "" const projectId = input.request.headers.get("x-opencode-project") ?? "" const ocClient = input.request.headers.get("x-opencode-client") ?? "" + const userAgent = input.request.headers.get("user-agent") ?? "" logger.metric({ is_stream: isStream, session: sessionId, request: requestId, client: ocClient, - ...(model === "mimo-v2-pro-free" && JSON.stringify(body).length < 1000 ? { payload: JSON.stringify(body) } : {}), + user_agent: userAgent, }) const zenData = ZenData.list(opts.modelList) const modelInfo = validateModel(zenData, model) From fc6d4b40102f802425511e2deffb8f0195bc986d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 25 Apr 2026 23:50:49 -0400 Subject: [PATCH 05/34] core: Add User-Agent header to identify client version in HTTP requests --- packages/opencode/src/session/llm.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index d5ff4e61c9..7a225ba9ee 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -373,6 +373,7 @@ const live: Layer.Layer< "x-opencode-session": input.sessionID, "x-opencode-request": input.user.id, "x-opencode-client": Flag.OPENCODE_CLIENT, + "User-Agent": `opencode/${InstallationVersion}`, } : { "x-session-affinity": input.sessionID, From e7053c41f42900f62ca8002713c4c8b521caf0ed Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:05:16 -0400 Subject: [PATCH 06/34] fix: bump openrouter sdk version to resolve deepseek reasoning issue (bug was in sdk pkg) (#24435) --- bun.lock | 6 +- packages/opencode/package.json | 2 +- packages/opencode/src/provider/transform.ts | 6 +- .../opencode/test/session/message-v2.test.ts | 73 +++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index 1d2e4462f3..6a146d9e84 100644 --- a/bun.lock +++ b/bun.lock @@ -393,7 +393,7 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@openrouter/ai-sdk-provider": "2.5.1", + "@openrouter/ai-sdk-provider": "2.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", @@ -1585,7 +1585,7 @@ "@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"], - "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="], + "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -5723,6 +5723,8 @@ "ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="], + "ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="], + "ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 98a707f4b9..df0043b066 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -115,7 +115,7 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@openrouter/ai-sdk-provider": "2.5.1", + "@openrouter/ai-sdk-provider": "2.8.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 67b02c0896..b2e9b59a3a 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -193,7 +193,11 @@ function normalizeMessages( }) } - if (typeof model.capabilities.interleaved === "object" && model.capabilities.interleaved.field) { + if ( + typeof model.capabilities.interleaved === "object" && + model.capabilities.interleaved.field && + model.api.npm !== "@openrouter/ai-sdk-provider" + ) { const field = model.capabilities.interleaved.field return msgs.map((msg) => { if (msg.role === "assistant" && Array.isArray(msg.content)) { diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts index abada013df..9591a5d623 100644 --- a/packages/opencode/test/session/message-v2.test.ts +++ b/packages/opencode/test/session/message-v2.test.ts @@ -873,6 +873,79 @@ describe("session.message-v2.toModelMessage", () => { ]) }) + test("preserves OpenRouter reasoning details through provider transform", async () => { + const assistantID = "m-assistant" + const openrouterModel: Provider.Model = { + ...model, + id: ModelID.make("deepseek/deepseek-v4-pro"), + providerID: ProviderID.make("openrouter"), + api: { + id: "deepseek/deepseek-v4-pro", + url: "https://openrouter.ai/api/v1", + npm: "@openrouter/ai-sdk-provider", + }, + capabilities: { + ...model.capabilities, + reasoning: true, + interleaved: { field: "reasoning_details" }, + }, + } + const reasoningDetails = [ + { + type: "reasoning.text", + text: "thinking", + format: "unknown", + index: 0, + }, + ] + const input: MessageV2.WithParts[] = [ + { + info: assistantInfo(assistantID, "m-parent", undefined, { + providerID: openrouterModel.providerID, + modelID: openrouterModel.id, + }), + parts: [ + { + ...basePart(assistantID, "a1"), + type: "reasoning", + text: "thinking", + time: { start: 0 }, + metadata: { + openrouter: { + reasoning_details: reasoningDetails, + }, + }, + }, + { + ...basePart(assistantID, "a2"), + type: "text", + text: "answer", + }, + ] as MessageV2.Part[], + }, + ] + + expect( + ProviderTransform.message(await MessageV2.toModelMessages(input, openrouterModel), openrouterModel, {}), + ).toStrictEqual([ + { + role: "assistant", + content: [ + { + type: "reasoning", + text: "thinking", + providerOptions: { + openrouter: { + reasoning_details: reasoningDetails, + }, + }, + }, + { type: "text", text: "answer" }, + ], + }, + ]) + }) + test("splits assistant messages on step-start boundaries", async () => { const assistantID = "m-assistant" From 95c43fc675906aca06a2c0a39b7fc5d5fb3e60d2 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 05:19:08 +0000 Subject: [PATCH 07/34] chore: update nix node_modules hashes --- nix/hashes.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nix/hashes.json b/nix/hashes.json index 84fb57a1ca..3562cf7024 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-LpzWEZzURUEj7fcHGvh33gM7D9GNPE+XIvU0/hmdcQM=", - "aarch64-linux": "sha256-0zdO3zuj6g9cMZFEOsvQJcKKcPjGVZJ2DkJdDcb2VCM=", - "aarch64-darwin": "sha256-dmT8R9Pmzh5tjO8NCCCtENiQpJQeifQpVdhaty1MXOs=", - "x86_64-darwin": "sha256-Q6rAQRoC6WaMAQl++YHAZmbNuO303cWgGaYzXaRlzy4=" + "x86_64-linux": "sha256-I5qF+zaPXIXlPWjI54XlsZ42E5EgUWbo74/ClaoJnNY=", + "aarch64-linux": "sha256-2yqFliYylXy3Lnf95nO/enefCrmP8GOfqKjnstdaMu4=", + "aarch64-darwin": "sha256-/Rk2Q6bPxOzD24HPQOe/EXlcEb7eGLGHi3P3iSqHaTU=", + "x86_64-darwin": "sha256-sKyoY9Z7858S0KG/mDhuGZLR2P7AdK+zy0xUVEJ3bt0=" } } From 5a5a2e5fa0c9f1ee965f3eaff5c787a856dd1734 Mon Sep 17 00:00:00 2001 From: Ariane Emory <97994360+ariane-emory@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:26:48 -0400 Subject: [PATCH 08/34] fix: correct typo in comment (#24420) --- nix/opencode.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/opencode.nix b/nix/opencode.nix index b629d0b554..7b06330fcb 100644 --- a/nix/opencode.nix +++ b/nix/opencode.nix @@ -64,7 +64,7 @@ stdenvNoCC.mkDerivation (finalAttrs: { [ ripgrep ] - # bun runs sysctl to detect if dunning on rosetta2 + # bun runs sysctl to detect if running on rosetta2 ++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl ) } From 6daa2b9aebab065a36dd2b9588ee2436e40ea66f Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 10:47:24 -0400 Subject: [PATCH 09/34] feat(httpapi): bridge experimental session list (#24478) --- packages/opencode/specs/effect/http-api.md | 6 +-- .../routes/instance/httpapi/experimental.ts | 46 ++++++++++++++++++ .../src/server/routes/instance/index.ts | 1 + .../test/server/httpapi-experimental.test.ts | 47 +++++++++++++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index a336804600..34c10d9377 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -181,7 +181,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho | `mcp` | `bridged` | status, add, OAuth, connect/disconnect | | `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | | top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` partial | console, tool, worktree list/mutations, resource list; global session list remains later | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | | `session` | `later/special` | large stateful surface plus streaming | | `sync` | `later` | process/control side effects | | `event` | `special` | SSE | @@ -266,7 +266,7 @@ This checklist tracks bridge parity only. Checked routes are available through t - [x] `POST /experimental/worktree` - create worktree. - [x] `DELETE /experimental/worktree` - remove worktree. - [x] `POST /experimental/worktree/reset` - reset worktree. -- [ ] `GET /experimental/session` - global session list. +- [x] `GET /experimental/session` - global session list. - [x] `GET /experimental/resource` - MCP resources. ### Workspace Routes @@ -351,7 +351,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 2. [x] Bridge MCP add/connect/disconnect routes. 3. [x] Bridge MCP OAuth routes: start, callback, authenticate, remove. 4. [x] Bridge experimental console switch and tool list routes. -5. [ ] Bridge experimental global session list. +5. [x] Bridge experimental global session list. 6. [ ] Bridge workspace create/remove/session-restore routes. 7. [ ] Bridge sync start/replay/history routes. 8. [ ] Bridge session read routes: list, status, get, children, todo, diff, messages. diff --git a/packages/opencode/src/server/routes/instance/httpapi/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/experimental.ts index 392302d429..f11191d64d 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/experimental.ts @@ -6,10 +6,12 @@ import { InstanceState } from "@/effect" import { MCP } from "@/mcp" import { Project } from "@/project" import { ProviderID, ModelID } from "@/provider/schema" +import { Session } from "@/session" import { ToolRegistry } from "@/tool" import * as EffectZod from "@/util/effect-zod" import { Worktree } from "@/worktree" import { Effect, Layer, Option, Schema } from "effect" +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse" import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "./auth" @@ -50,6 +52,15 @@ const ToolListQuery = Schema.Struct({ }) const WorktreeList = Schema.Array(Schema.String).annotate({ identifier: "WorktreeList" }) +const SessionListQuery = Schema.Struct({ + directory: Schema.optional(Schema.String), + roots: Schema.optional(Schema.Literals(["true", "false"])), + start: Schema.optional(Schema.NumberFromString), + cursor: Schema.optional(Schema.NumberFromString), + search: Schema.optional(Schema.String), + limit: Schema.optional(Schema.NumberFromString), + archived: Schema.optional(Schema.Literals(["true", "false"])), +}) export const ExperimentalPaths = { console: "/experimental/console", @@ -59,6 +70,7 @@ export const ExperimentalPaths = { toolIDs: "/experimental/tool/ids", worktree: "/experimental/worktree", worktreeReset: "/experimental/worktree/reset", + session: "/experimental/session", resource: "/experimental/resource", } as const @@ -154,6 +166,17 @@ export const ExperimentalApi = HttpApi.make("experimental") description: "Reset a worktree branch to the primary default branch.", }), ), + HttpApiEndpoint.get("session", ExperimentalPaths.session, { + query: SessionListQuery, + success: Schema.Array(Session.GlobalInfo), + }).annotateMerge( + OpenApi.annotations({ + identifier: "experimental.session.list", + summary: "List sessions", + description: + "Get a list of all OpenCode sessions across projects, sorted by most recently updated. Archived sessions are excluded by default.", + }), + ), HttpApiEndpoint.get("resource", ExperimentalPaths.resource, { success: Schema.Record(Schema.String, MCP.Resource), }).annotateMerge( @@ -279,6 +302,28 @@ export const experimentalHandlers = Layer.unwrap( return true }) + const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) { + const limit = ctx.query.limit ?? 100 + const sessions = Array.from( + Session.listGlobal({ + directory: ctx.query.directory, + roots: ctx.query.roots === "true" ? true : undefined, + start: ctx.query.start, + cursor: ctx.query.cursor, + search: ctx.query.search, + limit: limit + 1, + archived: ctx.query.archived === "true" ? true : undefined, + }), + ) + const list = sessions.length > limit ? sessions.slice(0, limit) : sessions + return HttpServerResponse.jsonUnsafe(list, { + headers: + sessions.length > limit && list.length > 0 + ? { "x-next-cursor": String(list[list.length - 1].time.updated) } + : undefined, + }) + }) + const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () { return yield* mcp.resources() }) @@ -294,6 +339,7 @@ export const experimentalHandlers = Layer.unwrap( .handle("worktreeCreate", worktreeCreate) .handle("worktreeRemove", worktreeRemove) .handle("worktreeReset", worktreeReset) + .handle("session", session) .handle("resource", resource), ) }), diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index b99aa948e8..58e64443d3 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -56,6 +56,7 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.post(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context)) app.delete(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context)) app.post(ExperimentalPaths.worktreeReset, (c) => handler(c.req.raw, context)) + app.get(ExperimentalPaths.session, (c) => handler(c.req.raw, context)) app.get(ExperimentalPaths.resource, (c) => handler(c.req.raw, context)) app.get("/provider", (c) => handler(c.req.raw, context)) app.get("/provider/auth", (c) => handler(c.req.raw, context)) diff --git a/packages/opencode/test/server/httpapi-experimental.test.ts b/packages/opencode/test/server/httpapi-experimental.test.ts index cf673dc969..3843012804 100644 --- a/packages/opencode/test/server/httpapi-experimental.test.ts +++ b/packages/opencode/test/server/httpapi-experimental.test.ts @@ -1,10 +1,12 @@ import { afterEach, describe, expect, test } from "bun:test" import type { UpgradeWebSocket } from "hono/ws" +import { Effect } from "effect" import { Flag } from "@opencode-ai/core/flag/flag" import { GlobalBus } from "@/bus/global" import { Instance } from "../../src/project/instance" import { InstanceRoutes } from "../../src/server/routes/instance" import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/experimental" +import { Session } from "../../src/session" import { Database } from "../../src/storage" import { Log } from "../../src/util" import { Worktree } from "../../src/worktree" @@ -22,6 +24,14 @@ function app() { return InstanceRoutes(websocket) } +function runSession(fx: Effect.Effect) { + return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +} + +function createSession(input?: Session.CreateInput) { + return runSession(Session.Service.use((svc) => svc.create(input))) +} + async function waitReady(directory: string) { return await new Promise((resolve, reject) => { const timer = setTimeout(() => { @@ -126,6 +136,43 @@ describe("experimental HttpApi", () => { expect(await switched.json()).toBe(true) }) + test("serves global session list through Hono bridge", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + + const first = await Instance.provide({ + directory: tmp.path, + fn: async () => createSession({ title: "page-one" }), + }) + await new Promise((resolve) => setTimeout(resolve, 5)) + const second = await Instance.provide({ + directory: tmp.path, + fn: async () => createSession({ title: "page-two" }), + }) + + const headers = { "x-opencode-directory": tmp.path } + const page = await app().request( + `${ExperimentalPaths.session}?${new URLSearchParams({ directory: tmp.path, limit: "1" })}`, + { headers }, + ) + expect(page.status).toBe(200) + expect(page.headers.get("x-next-cursor")).toBeTruthy() + + const body = (await page.json()) as Session.GlobalInfo[] + expect(body.map((session) => session.id)).toEqual([second.id]) + expect(body[0].project?.id).toBe(second.projectID) + + const next = await app().request( + `${ExperimentalPaths.session}?${new URLSearchParams({ + directory: tmp.path, + limit: "10", + cursor: body[0].time.updated.toString(), + })}`, + { headers }, + ) + expect(next.status).toBe(200) + expect(((await next.json()) as Session.GlobalInfo[]).map((session) => session.id)).toContain(first.id) + }) + testWorktreeMutations("serves worktree mutations through Hono bridge", async () => { await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) From 37c5eab6f8ccd5a64bad76865b4a687cd6721a27 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 14:48:31 +0000 Subject: [PATCH 10/34] chore: generate --- packages/opencode/specs/effect/http-api.md | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 34c10d9377..c2f51381ac 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `later` | process/control side effects | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | -------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `later` | process/control side effects | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist From aa5999b188e6f2ec18a374eb277d7a4f23faa6ca Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 11:12:04 -0400 Subject: [PATCH 11/34] feat(httpapi): bridge workspace mutations (#24483) --- packages/opencode/specs/effect/http-api.md | 42 ++++----- .../routes/instance/httpapi/workspace.ts | 86 ++++++++++++++++++- packages/opencode/src/server/server.ts | 3 + .../test/server/httpapi-workspace.test.ts | 86 +++++++++++++++++-- 4 files changed, 189 insertions(+), 28 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index c2f51381ac..9b84c799e5 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | -------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` partial | adaptor/list/status; create/remove/session-restore remain | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `later` | process/control side effects | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `later` | process/control side effects | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist @@ -272,11 +272,11 @@ This checklist tracks bridge parity only. Checked routes are available through t ### Workspace Routes - [x] `GET /experimental/workspace/adaptor` - list workspace adaptors. -- [ ] `POST /experimental/workspace` - create workspace. +- [x] `POST /experimental/workspace` - create workspace. - [x] `GET /experimental/workspace` - list workspaces. - [x] `GET /experimental/workspace/status` - workspace status. -- [ ] `DELETE /experimental/workspace/:id` - remove workspace. -- [ ] `POST /experimental/workspace/:id/session-restore` - restore session into workspace. +- [x] `DELETE /experimental/workspace/:id` - remove workspace. +- [x] `POST /experimental/workspace/:id/session-restore` - restore session into workspace. ### Sync Routes @@ -352,7 +352,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 3. [x] Bridge MCP OAuth routes: start, callback, authenticate, remove. 4. [x] Bridge experimental console switch and tool list routes. 5. [x] Bridge experimental global session list. -6. [ ] Bridge workspace create/remove/session-restore routes. +6. [x] Bridge workspace create/remove/session-restore routes. 7. [ ] Bridge sync start/replay/history routes. 8. [ ] Bridge session read routes: list, status, get, children, todo, diff, messages. 9. [ ] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. diff --git a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts index 2ab6b03d24..6454af4a32 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts @@ -2,15 +2,28 @@ import { listAdaptors } from "@/control-plane/adaptors" import { Workspace } from "@/control-plane/workspace" import { WorkspaceAdaptorEntry } from "@/control-plane/types" import * as InstanceState from "@/effect/instance-state" -import { Effect, Layer, Schema } from "effect" +import { Instance } from "@/project/instance" +import { Effect, Layer, Schema, Struct } from "effect" import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "./auth" const root = "/experimental/workspace" +const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"])).annotate({ + identifier: "WorkspaceCreateInput", +}) +const SessionRestorePayload = Schema.Struct(Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"])).annotate({ + identifier: "WorkspaceSessionRestoreInput", +}) +const SessionRestoreResponse = Schema.Struct({ + total: Schema.Number, +}).annotate({ identifier: "WorkspaceSessionRestoreResponse" }) + export const WorkspacePaths = { adaptors: `${root}/adaptor`, list: root, status: `${root}/status`, + remove: `${root}/:id`, + sessionRestore: `${root}/:id/session-restore`, } as const export const WorkspaceApi = HttpApi.make("workspace") @@ -35,6 +48,16 @@ export const WorkspaceApi = HttpApi.make("workspace") description: "List all workspaces.", }), ), + HttpApiEndpoint.post("create", WorkspacePaths.list, { + payload: CreatePayload, + success: Workspace.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "experimental.workspace.create", + summary: "Create workspace", + description: "Create a workspace for the current project.", + }), + ), HttpApiEndpoint.get("status", WorkspacePaths.status, { success: Schema.Array(Workspace.ConnectionStatus), }).annotateMerge( @@ -44,6 +67,27 @@ export const WorkspaceApi = HttpApi.make("workspace") description: "Get connection status for workspaces in the current project.", }), ), + HttpApiEndpoint.delete("remove", WorkspacePaths.remove, { + params: { id: Workspace.Info.fields.id }, + success: Schema.UndefinedOr(Workspace.Info), + }).annotateMerge( + OpenApi.annotations({ + identifier: "experimental.workspace.remove", + summary: "Remove workspace", + description: "Remove an existing workspace.", + }), + ), + HttpApiEndpoint.post("sessionRestore", WorkspacePaths.sessionRestore, { + params: { id: Workspace.Info.fields.id }, + payload: SessionRestorePayload, + success: SessionRestoreResponse, + }).annotateMerge( + OpenApi.annotations({ + identifier: "experimental.workspace.sessionRestore", + summary: "Restore session into workspace", + description: "Replay a session's sync events into the target workspace in batches.", + }), + ), ) .annotateMerge( OpenApi.annotations({ @@ -72,13 +116,51 @@ export const workspaceHandlers = Layer.unwrap( return Workspace.list((yield* InstanceState.context).project) }) + const create = Effect.fn("WorkspaceHttpApi.create")(function* (ctx: { payload: typeof CreatePayload.Type }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + Workspace.create({ + ...Schema.decodeUnknownSync(CreatePayload)(ctx.payload), + projectID: instance.project.id, + }), + ), + ) + }) + const status = Effect.fn("WorkspaceHttpApi.status")(function* () { const ids = new Set(Workspace.list((yield* InstanceState.context).project).map((item) => item.id)) return Workspace.status().filter((item) => ids.has(item.workspaceID)) }) + const remove = Effect.fn("WorkspaceHttpApi.remove")(function* (ctx: { params: { id: Workspace.Info["id"] } }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => Instance.restore(instance, () => Workspace.remove(ctx.params.id))) + }) + + const sessionRestore = Effect.fn("WorkspaceHttpApi.sessionRestore")(function* (ctx: { + params: { id: Workspace.Info["id"] } + payload: typeof SessionRestorePayload.Type + }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + Workspace.sessionRestore({ + workspaceID: ctx.params.id, + sessionID: ctx.payload.sessionID, + }), + ), + ) + }) + return HttpApiBuilder.group(WorkspaceApi, "workspace", (handlers) => - handlers.handle("adaptors", adaptors).handle("list", list).handle("status", status), + handlers + .handle("adaptors", adaptors) + .handle("list", list) + .handle("create", create) + .handle("status", status) + .handle("remove", remove) + .handle("sessionRestore", sessionRestore), ) }), ) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index fb278f268c..c493e4e70a 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -67,7 +67,10 @@ function create(opts: { cors?: string[] }) { const context = Context.empty() as Context.Context workspaceApp.get(WorkspacePaths.adaptors, (c) => handler(c.req.raw, context)) workspaceApp.get(WorkspacePaths.list, (c) => handler(c.req.raw, context)) + workspaceApp.post(WorkspacePaths.list, (c) => handler(c.req.raw, context)) workspaceApp.get(WorkspacePaths.status, (c) => handler(c.req.raw, context)) + workspaceApp.delete(WorkspacePaths.remove, (c) => handler(c.req.raw, context)) + workspaceApp.post(WorkspacePaths.sessionRestore, (c) => handler(c.req.raw, context)) } workspaceApp.route("/", workspaceLegacyApp) diff --git a/packages/opencode/test/server/httpapi-workspace.test.ts b/packages/opencode/test/server/httpapi-workspace.test.ts index 8256d8330f..d41dc73699 100644 --- a/packages/opencode/test/server/httpapi-workspace.test.ts +++ b/packages/opencode/test/server/httpapi-workspace.test.ts @@ -1,7 +1,14 @@ import { afterEach, describe, expect, test } from "bun:test" -import { Context } from "effect" +import { mkdir } from "node:fs/promises" +import path from "node:path" +import { Context, Effect } from "effect" +import { Flag } from "@opencode-ai/core/flag/flag" +import { registerAdaptor } from "../../src/control-plane/adaptors" +import type { WorkspaceAdaptor } from "../../src/control-plane/types" +import { Workspace } from "../../src/control-plane/workspace" import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/workspace" +import { Session } from "../../src/session" import { Log } from "../../src/util" import { resetDatabase } from "../fixture/db" import { tmpdir } from "../fixture/fixture" @@ -10,19 +17,50 @@ import { Instance } from "../../src/project/instance" void Log.init({ print: false }) const context = Context.empty() as Context.Context +const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES -function request(path: string, directory: string) { +function request(path: string, directory: string, init: RequestInit = {}) { + const headers = new Headers(init.headers) + headers.set("x-opencode-directory", directory) return ExperimentalHttpApiServer.webHandler().handler( new Request(`http://localhost${path}`, { - headers: { - "x-opencode-directory": directory, - }, + ...init, + headers, }), context, ) } +function runSession(fx: Effect.Effect) { + return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +} + +function localAdaptor(directory: string): WorkspaceAdaptor { + return { + name: "Local Test", + description: "Create a local test workspace", + configure(info) { + return { + ...info, + name: "local-test", + directory, + } + }, + async create() { + await mkdir(directory, { recursive: true }) + }, + async remove() {}, + target() { + return { + type: "local" as const, + directory, + } + }, + } +} + afterEach(async () => { + Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces await Instance.disposeAll() await resetDatabase() }) @@ -52,4 +90,42 @@ describe("workspace HttpApi", () => { expect(status.status).toBe(200) expect(await status.json()).toEqual([]) }) + + test("serves mutation endpoints", async () => { + Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => registerAdaptor(Instance.project.id, "local-test", localAdaptor(path.join(tmp.path, ".workspace"))), + }) + + const created = await request(WorkspacePaths.list, tmp.path, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ type: "local-test", branch: null, extra: null }), + }) + expect(created.status).toBe(200) + const workspace = (await created.json()) as Workspace.Info + expect(workspace).toMatchObject({ type: "local-test", name: "local-test" }) + + const session = await Instance.provide({ + directory: tmp.path, + fn: async () => runSession(Session.Service.use((svc) => svc.create({}))), + }) + const restored = await request(WorkspacePaths.sessionRestore.replace(":id", workspace.id), tmp.path, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ sessionID: session.id }), + }) + expect(restored.status).toBe(200) + expect((await restored.json()) as { total: number }).toMatchObject({ total: expect.any(Number) }) + + const removed = await request(WorkspacePaths.remove.replace(":id", workspace.id), tmp.path, { method: "DELETE" }) + expect(removed.status).toBe(200) + expect(await removed.json()).toMatchObject({ id: workspace.id }) + + const listed = await request(WorkspacePaths.list, tmp.path) + expect(listed.status).toBe(200) + expect(await listed.json()).toEqual([]) + }) }) From 854d4b7a53b14bdeefedfb413768802d4abc5acb Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 15:13:18 +0000 Subject: [PATCH 12/34] chore: generate --- packages/opencode/specs/effect/http-api.md | 34 +++++++++---------- .../routes/instance/httpapi/workspace.ts | 4 ++- .../test/server/httpapi-workspace.test.ts | 3 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 9b84c799e5..2a141d028d 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `later` | process/control side effects | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | -------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `later` | process/control side effects | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist diff --git a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts index 6454af4a32..c47a00d6b0 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts @@ -11,7 +11,9 @@ const root = "/experimental/workspace" const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"])).annotate({ identifier: "WorkspaceCreateInput", }) -const SessionRestorePayload = Schema.Struct(Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"])).annotate({ +const SessionRestorePayload = Schema.Struct( + Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]), +).annotate({ identifier: "WorkspaceSessionRestoreInput", }) const SessionRestoreResponse = Schema.Struct({ diff --git a/packages/opencode/test/server/httpapi-workspace.test.ts b/packages/opencode/test/server/httpapi-workspace.test.ts index d41dc73699..8fee8a8034 100644 --- a/packages/opencode/test/server/httpapi-workspace.test.ts +++ b/packages/opencode/test/server/httpapi-workspace.test.ts @@ -96,7 +96,8 @@ describe("workspace HttpApi", () => { await using tmp = await tmpdir({ git: true }) await Instance.provide({ directory: tmp.path, - fn: async () => registerAdaptor(Instance.project.id, "local-test", localAdaptor(path.join(tmp.path, ".workspace"))), + fn: async () => + registerAdaptor(Instance.project.id, "local-test", localAdaptor(path.join(tmp.path, ".workspace"))), }) const created = await request(WorkspacePaths.list, tmp.path, { From 7feb6ab9627000dd88082e3b05879833dc0a387a Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 26 Apr 2026 23:15:55 +0800 Subject: [PATCH 13/34] fix(docs): correct OpenCode Go DeepSeek endpoints (#24500) --- packages/web/src/content/docs/ar/go.mdx | 4 ++-- packages/web/src/content/docs/bs/go.mdx | 4 ++-- packages/web/src/content/docs/da/go.mdx | 4 ++-- packages/web/src/content/docs/de/go.mdx | 4 ++-- packages/web/src/content/docs/es/go.mdx | 4 ++-- packages/web/src/content/docs/fr/go.mdx | 4 ++-- packages/web/src/content/docs/go.mdx | 4 ++-- packages/web/src/content/docs/it/go.mdx | 4 ++-- packages/web/src/content/docs/ja/go.mdx | 4 ++-- packages/web/src/content/docs/ko/go.mdx | 4 ++-- packages/web/src/content/docs/nb/go.mdx | 4 ++-- packages/web/src/content/docs/pl/go.mdx | 4 ++-- packages/web/src/content/docs/pt-br/go.mdx | 4 ++-- packages/web/src/content/docs/ru/go.mdx | 4 ++-- packages/web/src/content/docs/th/go.mdx | 4 ++-- packages/web/src/content/docs/tr/go.mdx | 4 ++-- packages/web/src/content/docs/zh-cn/go.mdx | 4 ++-- packages/web/src/content/docs/zh-tw/go.mdx | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/web/src/content/docs/ar/go.mdx b/packages/web/src/content/docs/ar/go.mdx index 42ba87ffab..246e86edd8 100644 --- a/packages/web/src/content/docs/ar/go.mdx +++ b/packages/web/src/content/docs/ar/go.mdx @@ -140,8 +140,8 @@ OpenCode Go حاليًا في المرحلة التجريبية. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/bs/go.mdx b/packages/web/src/content/docs/bs/go.mdx index 0d183ba28e..c54350696f 100644 --- a/packages/web/src/content/docs/bs/go.mdx +++ b/packages/web/src/content/docs/bs/go.mdx @@ -152,8 +152,8 @@ Također možete pristupiti Go modelima putem sljedećih API endpointa. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/da/go.mdx b/packages/web/src/content/docs/da/go.mdx index fcdbaf5f9b..20b3ad207c 100644 --- a/packages/web/src/content/docs/da/go.mdx +++ b/packages/web/src/content/docs/da/go.mdx @@ -152,8 +152,8 @@ Du kan også få adgang til Go-modeller gennem følgende API-endpoints. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/de/go.mdx b/packages/web/src/content/docs/de/go.mdx index 09341efa4b..b79aa96abb 100644 --- a/packages/web/src/content/docs/de/go.mdx +++ b/packages/web/src/content/docs/de/go.mdx @@ -142,8 +142,8 @@ Du kannst auf die Go-Modelle auch über die folgenden API-Endpunkte zugreifen. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/es/go.mdx b/packages/web/src/content/docs/es/go.mdx index fd1edcf9ce..31ba3f45ff 100644 --- a/packages/web/src/content/docs/es/go.mdx +++ b/packages/web/src/content/docs/es/go.mdx @@ -152,8 +152,8 @@ También puedes acceder a los modelos de Go a través de los siguientes endpoint | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/fr/go.mdx b/packages/web/src/content/docs/fr/go.mdx index 80cd5d2fc2..51897e8a42 100644 --- a/packages/web/src/content/docs/fr/go.mdx +++ b/packages/web/src/content/docs/fr/go.mdx @@ -140,8 +140,8 @@ Vous pouvez également accéder aux modèles Go via les points de terminaison d' | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/go.mdx b/packages/web/src/content/docs/go.mdx index 9db12a644c..8e85fb03cc 100644 --- a/packages/web/src/content/docs/go.mdx +++ b/packages/web/src/content/docs/go.mdx @@ -152,8 +152,8 @@ You can also access Go models through the following API endpoints. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/it/go.mdx b/packages/web/src/content/docs/it/go.mdx index 775e567f8e..03c1c10ca6 100644 --- a/packages/web/src/content/docs/it/go.mdx +++ b/packages/web/src/content/docs/it/go.mdx @@ -150,8 +150,8 @@ Puoi anche accedere ai modelli Go tramite i seguenti endpoint API. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/ja/go.mdx b/packages/web/src/content/docs/ja/go.mdx index 6ab9c3235c..1a110a700c 100644 --- a/packages/web/src/content/docs/ja/go.mdx +++ b/packages/web/src/content/docs/ja/go.mdx @@ -140,8 +140,8 @@ Zen残高にクレジットがある場合は、コンソールで**Use balance* | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/ko/go.mdx b/packages/web/src/content/docs/ko/go.mdx index 83c7c8e6a6..445cd86578 100644 --- a/packages/web/src/content/docs/ko/go.mdx +++ b/packages/web/src/content/docs/ko/go.mdx @@ -140,8 +140,8 @@ Zen 잔액에 크레딧도 있다면, console에서 **Use balance** 옵션을 | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/nb/go.mdx b/packages/web/src/content/docs/nb/go.mdx index 53ad74f376..ab3676d6d1 100644 --- a/packages/web/src/content/docs/nb/go.mdx +++ b/packages/web/src/content/docs/nb/go.mdx @@ -152,8 +152,8 @@ Du kan også få tilgang til Go-modeller gjennom følgende API-endepunkter. | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/pl/go.mdx b/packages/web/src/content/docs/pl/go.mdx index 87fcf1cc35..b984f08590 100644 --- a/packages/web/src/content/docs/pl/go.mdx +++ b/packages/web/src/content/docs/pl/go.mdx @@ -144,8 +144,8 @@ Możesz również uzyskać dostęp do modeli Go za pośrednictwem następującyc | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/pt-br/go.mdx b/packages/web/src/content/docs/pt-br/go.mdx index 4a22089339..2094b5a57a 100644 --- a/packages/web/src/content/docs/pt-br/go.mdx +++ b/packages/web/src/content/docs/pt-br/go.mdx @@ -152,8 +152,8 @@ Você também pode acessar os modelos do Go através dos seguintes endpoints de | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/ru/go.mdx b/packages/web/src/content/docs/ru/go.mdx index 5a07418a73..0c257153bd 100644 --- a/packages/web/src/content/docs/ru/go.mdx +++ b/packages/web/src/content/docs/ru/go.mdx @@ -152,8 +152,8 @@ OpenCode Go включает следующие лимиты: | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/th/go.mdx b/packages/web/src/content/docs/th/go.mdx index 17cd9feb84..153f61368d 100644 --- a/packages/web/src/content/docs/th/go.mdx +++ b/packages/web/src/content/docs/th/go.mdx @@ -140,8 +140,8 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้: | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/tr/go.mdx b/packages/web/src/content/docs/tr/go.mdx index 884c0e00cf..77b0eb8c7f 100644 --- a/packages/web/src/content/docs/tr/go.mdx +++ b/packages/web/src/content/docs/tr/go.mdx @@ -140,8 +140,8 @@ Go modellerine aşağıdaki API uç noktaları aracılığıyla da erişebilirsi | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/zh-cn/go.mdx b/packages/web/src/content/docs/zh-cn/go.mdx index 828da590a3..04156059a7 100644 --- a/packages/web/src/content/docs/zh-cn/go.mdx +++ b/packages/web/src/content/docs/zh-cn/go.mdx @@ -140,8 +140,8 @@ OpenCode Go 包含以下限制: | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | diff --git a/packages/web/src/content/docs/zh-tw/go.mdx b/packages/web/src/content/docs/zh-tw/go.mdx index 1df68a6c4e..a466ccba3b 100644 --- a/packages/web/src/content/docs/zh-tw/go.mdx +++ b/packages/web/src/content/docs/zh-tw/go.mdx @@ -140,8 +140,8 @@ OpenCode Go 包含以下限制: | GLM-5 | glm-5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | -| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` | +| DeepSeek V4 Pro | deepseek-v4-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| DeepSeek V4 Flash | deepseek-v4-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Pro | mimo-v2-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2-Omni | mimo-v2-omni | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` | From d03e6cedde09d3e7ec31ea944d774176160c5b78 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 26 Apr 2026 11:18:47 -0400 Subject: [PATCH 14/34] ci: update team assignments in github-triage Update team member assignments in the triage tool: - Remove thdxr from tui and core teams - Add simonklee to tui team - Add kitlangton to core team --- .opencode/tool/github-triage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts index dcbfc8d054..56886808a4 100644 --- a/.opencode/tool/github-triage.ts +++ b/.opencode/tool/github-triage.ts @@ -3,8 +3,8 @@ import { tool } from "@opencode-ai/plugin" const TEAM = { desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"], zen: ["fwang", "MrMushrooooom"], - tui: ["thdxr", "kommander", "rekram1-node"], - core: ["thdxr", "rekram1-node", "jlongster"], + tui: ["kommander", "rekram1-node", "simonklee"], + core: ["kitlangton", "rekram1-node", "jlongster"], docs: ["R44VC0RP"], windows: ["Hona"], } as const From da61b0290a63cecaab5739431d9ed9eb9967e528 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 11:31:46 -0400 Subject: [PATCH 15/34] feat(httpapi): bridge sync routes (#24484) --- packages/opencode/specs/effect/http-api.md | 42 +++--- .../src/server/routes/instance/httpapi/mcp.ts | 7 +- .../server/routes/instance/httpapi/server.ts | 2 + .../server/routes/instance/httpapi/sync.ts | 130 ++++++++++++++++++ .../routes/instance/httpapi/workspace.ts | 2 +- .../src/server/routes/instance/index.ts | 4 + .../opencode/test/server/httpapi-sync.test.ts | 84 +++++++++++ 7 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 packages/opencode/src/server/routes/instance/httpapi/sync.ts create mode 100644 packages/opencode/test/server/httpapi-sync.test.ts diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 2a141d028d..932cf8f67e 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | -------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `later` | process/control side effects | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `bridged` | start/replay/history | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist @@ -280,9 +280,9 @@ This checklist tracks bridge parity only. Checked routes are available through t ### Sync Routes -- [ ] `POST /sync/start` - start workspace sync. -- [ ] `POST /sync/replay` - replay sync events. -- [ ] `POST /sync/history` - list sync event history. +- [x] `POST /sync/start` - start workspace sync. +- [x] `POST /sync/replay` - replay sync events. +- [x] `POST /sync/history` - list sync event history. ### Session Routes @@ -353,7 +353,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 4. [x] Bridge experimental console switch and tool list routes. 5. [x] Bridge experimental global session list. 6. [x] Bridge workspace create/remove/session-restore routes. -7. [ ] Bridge sync start/replay/history routes. +7. [x] Bridge sync start/replay/history routes. 8. [ ] Bridge session read routes: list, status, get, children, todo, diff, messages. 9. [ ] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. 10. [ ] Bridge session share/summary/message/part mutation routes. diff --git a/packages/opencode/src/server/routes/instance/httpapi/mcp.ts b/packages/opencode/src/server/routes/instance/httpapi/mcp.ts index 2abc8519a7..b7b8d512fc 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/mcp.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/mcp.ts @@ -139,9 +139,10 @@ export const mcpHandlers = Layer.unwrap( }) const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) { - const payload = Schema.decodeUnknownSync(AddPayload)(ctx.payload) - const result = (yield* mcp.add(payload.name, payload.config)).status - return Schema.decodeUnknownSync(StatusMap)("status" in result ? { [payload.name]: result } : result) + const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status + return yield* Schema.decodeUnknownEffect(StatusMap)("status" in result ? { [ctx.payload.name]: result } : result).pipe( + Effect.mapError(() => new HttpApiError.BadRequest({})), + ) }) const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) { diff --git a/packages/opencode/src/server/routes/instance/httpapi/server.ts b/packages/opencode/src/server/routes/instance/httpapi/server.ts index be574c914b..fa728b04e6 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/server.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/server.ts @@ -18,6 +18,7 @@ import { PermissionApi, permissionHandlers } from "./permission" import { ProjectApi, projectHandlers } from "./project" import { ProviderApi, providerHandlers } from "./provider" import { QuestionApi, questionHandlers } from "./question" +import { SyncApi, syncHandlers } from "./sync" import { WorkspaceApi, workspaceHandlers } from "./workspace" import { disposeMiddleware } from "./lifecycle" import { memoMap } from "@opencode-ai/core/effect/memo-map" @@ -73,6 +74,7 @@ export const routes = Layer.mergeAll( HttpApiBuilder.layer(QuestionApi).pipe(Layer.provide(questionHandlers)), HttpApiBuilder.layer(PermissionApi).pipe(Layer.provide(permissionHandlers)), HttpApiBuilder.layer(ProviderApi).pipe(Layer.provide(providerHandlers)), + HttpApiBuilder.layer(SyncApi).pipe(Layer.provide(syncHandlers)), HttpApiBuilder.layer(WorkspaceApi).pipe(Layer.provide(workspaceHandlers)), ).pipe( Layer.provide(authorizationLayer), diff --git a/packages/opencode/src/server/routes/instance/httpapi/sync.ts b/packages/opencode/src/server/routes/instance/httpapi/sync.ts new file mode 100644 index 0000000000..aa213cc167 --- /dev/null +++ b/packages/opencode/src/server/routes/instance/httpapi/sync.ts @@ -0,0 +1,130 @@ +import { startWorkspaceSyncing } from "@/control-plane/workspace" +import * as InstanceState from "@/effect/instance-state" +import { Database, asc, and, eq, lte, not, or } from "@/storage" +import { SyncEvent } from "@/sync" +import { EventTable } from "@/sync/event.sql" +import { Effect, Layer, Schema } from "effect" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { Authorization } from "./auth" + +const root = "/sync" +const ReplayEvent = Schema.Struct({ + id: Schema.String, + aggregateID: Schema.String, + seq: Schema.Number, + type: Schema.String, + data: Schema.Record(Schema.String, Schema.Unknown), +}).annotate({ identifier: "SyncReplayEvent" }) +const ReplayPayload = Schema.Struct({ + directory: Schema.String, + events: Schema.NonEmptyArray(ReplayEvent), +}).annotate({ identifier: "SyncReplayInput" }) +const ReplayResponse = Schema.Struct({ + sessionID: Schema.String, +}).annotate({ identifier: "SyncReplayResponse" }) +const HistoryPayload = Schema.Record(Schema.String, Schema.Number) +const HistoryEvent = Schema.Struct({ + id: Schema.String, + aggregate_id: Schema.String, + seq: Schema.Number, + type: Schema.String, + data: Schema.Record(Schema.String, Schema.Unknown), +}).annotate({ identifier: "SyncHistoryEvent" }) + +export const SyncPaths = { + start: `${root}/start`, + replay: `${root}/replay`, + history: `${root}/history`, +} as const + +export const SyncApi = HttpApi.make("sync") + .add( + HttpApiGroup.make("sync") + .add( + HttpApiEndpoint.post("start", SyncPaths.start, { + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "sync.start", + summary: "Start workspace sync", + description: "Start sync loops for workspaces in the current project that have active sessions.", + }), + ), + HttpApiEndpoint.post("replay", SyncPaths.replay, { + payload: ReplayPayload, + success: ReplayResponse, + }).annotateMerge( + OpenApi.annotations({ + identifier: "sync.replay", + summary: "Replay sync events", + description: "Validate and replay a complete sync event history.", + }), + ), + HttpApiEndpoint.post("history", SyncPaths.history, { + payload: HistoryPayload, + success: Schema.Array(HistoryEvent), + }).annotateMerge( + OpenApi.annotations({ + identifier: "sync.history.list", + summary: "List sync events", + description: + "List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.", + }), + ), + ) + .annotateMerge( + OpenApi.annotations({ + title: "sync", + description: "Experimental HttpApi sync routes.", + }), + ) + .middleware(Authorization), + ) + .annotateMerge( + OpenApi.annotations({ + title: "opencode experimental HttpApi", + version: "0.0.1", + description: "Experimental HttpApi surface for selected instance routes.", + }), + ) + +export const syncHandlers = Layer.unwrap( + Effect.gen(function* () { + const start = Effect.fn("SyncHttpApi.start")(function* () { + startWorkspaceSyncing((yield* InstanceState.context).project.id) + return true + }) + + const replay = Effect.fn("SyncHttpApi.replay")(function* (ctx: { payload: typeof ReplayPayload.Type }) { + const events: SyncEvent.SerializedEvent[] = ctx.payload.events.map((event) => ({ + id: event.id, + aggregateID: event.aggregateID, + seq: event.seq, + type: event.type, + data: { ...event.data }, + })) + SyncEvent.replayAll(events) + return { sessionID: events[0].aggregateID } + }) + + const history = Effect.fn("SyncHttpApi.history")(function* (ctx: { payload: typeof HistoryPayload.Type }) { + const exclude = Object.entries(ctx.payload) + return Database.use((db) => + db + .select() + .from(EventTable) + .where( + exclude.length > 0 + ? not(or(...exclude.map(([id, seq]) => and(eq(EventTable.aggregate_id, id), lte(EventTable.seq, seq))))!) + : undefined, + ) + .orderBy(asc(EventTable.seq)) + .all(), + ) + }) + + return HttpApiBuilder.group(SyncApi, "sync", (handlers) => + handlers.handle("start", start).handle("replay", replay).handle("history", history), + ) + }), +) diff --git a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts index c47a00d6b0..c269596011 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/workspace.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/workspace.ts @@ -123,7 +123,7 @@ export const workspaceHandlers = Layer.unwrap( return yield* Effect.promise(() => Instance.restore(instance, () => Workspace.create({ - ...Schema.decodeUnknownSync(CreatePayload)(ctx.payload), + ...ctx.payload, projectID: instance.project.id, }), ), diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index 58e64443d3..57988b26f1 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -20,6 +20,7 @@ import { ExperimentalPaths } from "./httpapi/experimental" import { FilePaths } from "./httpapi/file" import { InstancePaths } from "./httpapi/instance" import { McpPaths } from "./httpapi/mcp" +import { SyncPaths } from "./httpapi/sync" import { ProjectRoutes } from "./project" import { SessionRoutes } from "./session" import { PtyRoutes } from "./pty" @@ -89,6 +90,9 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.delete(McpPaths.auth, (c) => handler(c.req.raw, context)) app.post(McpPaths.connect, (c) => handler(c.req.raw, context)) app.post(McpPaths.disconnect, (c) => handler(c.req.raw, context)) + app.post(SyncPaths.start, (c) => handler(c.req.raw, context)) + app.post(SyncPaths.replay, (c) => handler(c.req.raw, context)) + app.post(SyncPaths.history, (c) => handler(c.req.raw, context)) } return app diff --git a/packages/opencode/test/server/httpapi-sync.test.ts b/packages/opencode/test/server/httpapi-sync.test.ts new file mode 100644 index 0000000000..d48d7608aa --- /dev/null +++ b/packages/opencode/test/server/httpapi-sync.test.ts @@ -0,0 +1,84 @@ +import { afterEach, describe, expect, test } from "bun:test" +import type { UpgradeWebSocket } from "hono/ws" +import { Effect } from "effect" +import { Flag } from "@opencode-ai/core/flag/flag" +import { Instance } from "../../src/project/instance" +import { InstanceRoutes } from "../../src/server/routes/instance" +import { SyncPaths } from "../../src/server/routes/instance/httpapi/sync" +import { Session } from "../../src/session" +import { Log } from "../../src/util" +import { resetDatabase } from "../fixture/db" +import { tmpdir } from "../fixture/fixture" + +void Log.init({ print: false }) + +const originalHttpApi = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI +const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES +const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket + +function app() { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true + return InstanceRoutes(websocket) +} + +function runSession(fx: Effect.Effect) { + return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +} + +afterEach(async () => { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = originalHttpApi + Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces + await Instance.disposeAll() + await resetDatabase() +}) + +describe("sync HttpApi", () => { + test("serves sync routes through Hono bridge", async () => { + Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } + + const session = await Instance.provide({ + directory: tmp.path, + fn: async () => runSession(Session.Service.use((svc) => svc.create({ title: "sync" }))), + }) + + const started = await app().request(SyncPaths.start, { method: "POST", headers }) + expect(started.status).toBe(200) + expect(await started.json()).toBe(true) + + const history = await app().request(SyncPaths.history, { + method: "POST", + headers, + body: JSON.stringify({}), + }) + expect(history.status).toBe(200) + const rows = (await history.json()) as Array<{ + id: string + aggregate_id: string + seq: number + type: string + data: Record + }> + expect(rows.map((row) => row.aggregate_id)).toContain(session.id) + + const replayed = await app().request(SyncPaths.replay, { + method: "POST", + headers, + body: JSON.stringify({ + directory: tmp.path, + events: rows + .filter((row) => row.aggregate_id === session.id) + .map((row) => ({ + id: row.id, + aggregateID: row.aggregate_id, + seq: row.seq, + type: row.type, + data: row.data, + })), + }), + }) + expect(replayed.status).toBe(200) + expect(await replayed.json()).toEqual({ sessionID: session.id }) + }) +}) From de413c56ae543a9276e9019b64053a4f626d0aec Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 15:32:42 +0000 Subject: [PATCH 16/34] chore: generate --- packages/opencode/specs/effect/http-api.md | 34 +++++++++---------- .../src/server/routes/instance/httpapi/mcp.ts | 6 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 932cf8f67e..3a1a99860f 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `bridged` | start/replay/history | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | -------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `later/special` | large stateful surface plus streaming | +| `sync` | `bridged` | start/replay/history | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist diff --git a/packages/opencode/src/server/routes/instance/httpapi/mcp.ts b/packages/opencode/src/server/routes/instance/httpapi/mcp.ts index b7b8d512fc..692187d735 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/mcp.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/mcp.ts @@ -140,9 +140,9 @@ export const mcpHandlers = Layer.unwrap( const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) { const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status - return yield* Schema.decodeUnknownEffect(StatusMap)("status" in result ? { [ctx.payload.name]: result } : result).pipe( - Effect.mapError(() => new HttpApiError.BadRequest({})), - ) + return yield* Schema.decodeUnknownEffect(StatusMap)( + "status" in result ? { [ctx.payload.name]: result } : result, + ).pipe(Effect.mapError(() => new HttpApiError.BadRequest({}))) }) const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) { From e0d1ff42c0df9041a204cfb0755a0d259b35de3c Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 11:49:11 -0400 Subject: [PATCH 17/34] feat(httpapi): bridge session read routes (#24485) --- packages/opencode/specs/effect/http-api.md | 52 ++-- .../server/routes/instance/httpapi/server.ts | 2 + .../server/routes/instance/httpapi/session.ts | 242 ++++++++++++++++++ .../src/server/routes/instance/index.ts | 9 + .../test/server/httpapi-session.test.ts | 108 ++++++++ 5 files changed, 387 insertions(+), 26 deletions(-) create mode 100644 packages/opencode/src/server/routes/instance/httpapi/session.ts create mode 100644 packages/opencode/test/server/httpapi-session.test.ts diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 3a1a99860f..fc17feb5b3 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | -------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `later/special` | large stateful surface plus streaming | -| `sync` | `bridged` | start/replay/history | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `bridged` partial | read routes; lifecycle, message mutations, streaming remain | +| `sync` | `bridged` | start/replay/history | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist @@ -286,11 +286,11 @@ This checklist tracks bridge parity only. Checked routes are available through t ### Session Routes -- [ ] `GET /session` - list sessions. -- [ ] `GET /session/status` - session status map. -- [ ] `GET /session/:sessionID` - get session. -- [ ] `GET /session/:sessionID/children` - get child sessions. -- [ ] `GET /session/:sessionID/todo` - get session todos. +- [x] `GET /session` - list sessions. +- [x] `GET /session/status` - session status map. +- [x] `GET /session/:sessionID` - get session. +- [x] `GET /session/:sessionID/children` - get child sessions. +- [x] `GET /session/:sessionID/todo` - get session todos. - [ ] `POST /session` - create session. - [ ] `DELETE /session/:sessionID` - delete session. - [ ] `PATCH /session/:sessionID` - update session metadata. @@ -298,11 +298,11 @@ This checklist tracks bridge parity only. Checked routes are available through t - [ ] `POST /session/:sessionID/fork` - fork session. - [ ] `POST /session/:sessionID/abort` - abort session. - [ ] `POST /session/:sessionID/share` - share session. -- [ ] `GET /session/:sessionID/diff` - session diff. +- [x] `GET /session/:sessionID/diff` - session diff. - [ ] `DELETE /session/:sessionID/share` - unshare session. - [ ] `POST /session/:sessionID/summarize` - summarize session. -- [ ] `GET /session/:sessionID/message` - list session messages. -- [ ] `GET /session/:sessionID/message/:messageID` - get message. +- [x] `GET /session/:sessionID/message` - list session messages. +- [x] `GET /session/:sessionID/message/:messageID` - get message. - [ ] `DELETE /session/:sessionID/message/:messageID` - delete message. - [ ] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part. - [ ] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part. @@ -354,7 +354,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 5. [x] Bridge experimental global session list. 6. [x] Bridge workspace create/remove/session-restore routes. 7. [x] Bridge sync start/replay/history routes. -8. [ ] Bridge session read routes: list, status, get, children, todo, diff, messages. +8. [x] Bridge session read routes: list, status, get, children, todo, diff, messages. 9. [ ] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. 10. [ ] Bridge session share/summary/message/part mutation routes. 11. [ ] Replace event SSE with non-Hono Effect HTTP. diff --git a/packages/opencode/src/server/routes/instance/httpapi/server.ts b/packages/opencode/src/server/routes/instance/httpapi/server.ts index fa728b04e6..adc70a43b3 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/server.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/server.ts @@ -18,6 +18,7 @@ import { PermissionApi, permissionHandlers } from "./permission" import { ProjectApi, projectHandlers } from "./project" import { ProviderApi, providerHandlers } from "./provider" import { QuestionApi, questionHandlers } from "./question" +import { SessionApi, sessionHandlers } from "./session" import { SyncApi, syncHandlers } from "./sync" import { WorkspaceApi, workspaceHandlers } from "./workspace" import { disposeMiddleware } from "./lifecycle" @@ -74,6 +75,7 @@ export const routes = Layer.mergeAll( HttpApiBuilder.layer(QuestionApi).pipe(Layer.provide(questionHandlers)), HttpApiBuilder.layer(PermissionApi).pipe(Layer.provide(permissionHandlers)), HttpApiBuilder.layer(ProviderApi).pipe(Layer.provide(providerHandlers)), + HttpApiBuilder.layer(SessionApi).pipe(Layer.provide(sessionHandlers)), HttpApiBuilder.layer(SyncApi).pipe(Layer.provide(syncHandlers)), HttpApiBuilder.layer(WorkspaceApi).pipe(Layer.provide(workspaceHandlers)), ).pipe( diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts new file mode 100644 index 0000000000..e06c8d98ac --- /dev/null +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -0,0 +1,242 @@ +import * as InstanceState from "@/effect/instance-state" +import { Instance } from "@/project/instance" +import { Session } from "@/session" +import { MessageV2 } from "@/session/message-v2" +import { SessionStatus } from "@/session/status" +import { SessionSummary } from "@/session/summary" +import { Todo } from "@/session/todo" +import { MessageID, SessionID } from "@/session/schema" +import { Snapshot } from "@/snapshot" +import { Effect, Layer, Schema, Struct } from "effect" +import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http" +import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { Authorization } from "./auth" + +const root = "/session" +const ListQuery = Schema.Struct({ + directory: Schema.optional(Schema.String), + roots: Schema.optional(Schema.Literals(["true", "false"])), + start: Schema.optional(Schema.NumberFromString), + search: Schema.optional(Schema.String), + limit: Schema.optional(Schema.NumberFromString), +}) +const DiffQuery = Schema.Struct(Struct.omit(SessionSummary.DiffInput.fields, ["sessionID"])) +const MessagesQuery = Schema.Struct({ + limit: Schema.optional(Schema.NumberFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))), + before: Schema.optional(Schema.String), +}) +const StatusMap = Schema.Record(Schema.String, SessionStatus.Info) + +export const SessionPaths = { + list: root, + status: `${root}/status`, + get: `${root}/:sessionID`, + children: `${root}/:sessionID/children`, + todo: `${root}/:sessionID/todo`, + diff: `${root}/:sessionID/diff`, + messages: `${root}/:sessionID/message`, + message: `${root}/:sessionID/message/:messageID`, +} as const + +export const SessionApi = HttpApi.make("session") + .add( + HttpApiGroup.make("session") + .add( + HttpApiEndpoint.get("list", SessionPaths.list, { + query: ListQuery, + success: Schema.Array(Session.Info), + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.list", + summary: "List sessions", + description: "Get a list of all OpenCode sessions, sorted by most recently updated.", + }), + ), + HttpApiEndpoint.get("status", SessionPaths.status, { + success: StatusMap, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.status", + summary: "Get session status", + description: "Retrieve the current status of all sessions, including active, idle, and completed states.", + }), + ), + HttpApiEndpoint.get("get", SessionPaths.get, { + params: { sessionID: SessionID }, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.get", + summary: "Get session", + description: "Retrieve detailed information about a specific OpenCode session.", + }), + ), + HttpApiEndpoint.get("children", SessionPaths.children, { + params: { sessionID: SessionID }, + success: Schema.Array(Session.Info), + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.children", + summary: "Get session children", + description: "Retrieve all child sessions that were forked from the specified parent session.", + }), + ), + HttpApiEndpoint.get("todo", SessionPaths.todo, { + params: { sessionID: SessionID }, + success: Schema.Array(Todo.Info), + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.todo", + summary: "Get session todos", + description: "Retrieve the todo list associated with a specific session, showing tasks and action items.", + }), + ), + HttpApiEndpoint.get("diff", SessionPaths.diff, { + params: { sessionID: SessionID }, + query: DiffQuery, + success: Schema.Array(Snapshot.FileDiff), + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.diff", + summary: "Get message diff", + description: "Get the file changes (diff) that resulted from a specific user message in the session.", + }), + ), + HttpApiEndpoint.get("messages", SessionPaths.messages, { + params: { sessionID: SessionID }, + query: MessagesQuery, + success: Schema.Array(MessageV2.WithParts), + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.messages", + summary: "Get session messages", + description: "Retrieve all messages in a session, including user prompts and AI responses.", + }), + ), + HttpApiEndpoint.get("message", SessionPaths.message, { + params: { sessionID: SessionID, messageID: MessageID }, + success: MessageV2.WithParts, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.message", + summary: "Get message", + description: "Retrieve a specific message from a session by its message ID.", + }), + ), + ) + .annotateMerge( + OpenApi.annotations({ + title: "session", + description: "Experimental HttpApi session routes.", + }), + ) + .middleware(Authorization), + ) + .annotateMerge( + OpenApi.annotations({ + title: "opencode experimental HttpApi", + version: "0.0.1", + description: "Experimental HttpApi surface for selected instance routes.", + }), + ) + +export const sessionHandlers = Layer.unwrap( + Effect.gen(function* () { + const session = yield* Session.Service + const statusSvc = yield* SessionStatus.Service + const todoSvc = yield* Todo.Service + const summary = yield* SessionSummary.Service + + const list = Effect.fn("SessionHttpApi.list")(function* (ctx: { query: typeof ListQuery.Type }) { + const instance = yield* InstanceState.context + return Instance.restore(instance, () => + Array.from( + Session.list({ + directory: ctx.query.directory, + roots: ctx.query.roots === "true" ? true : undefined, + start: ctx.query.start, + search: ctx.query.search, + limit: ctx.query.limit, + }), + ), + ) + }) + + const status = Effect.fn("SessionHttpApi.status")(function* () { + return Object.fromEntries(yield* statusSvc.list()) + }) + + const get = Effect.fn("SessionHttpApi.get")(function* (ctx: { params: { sessionID: SessionID } }) { + return yield* session.get(ctx.params.sessionID) + }) + + const children = Effect.fn("SessionHttpApi.children")(function* (ctx: { params: { sessionID: SessionID } }) { + return yield* session.children(ctx.params.sessionID) + }) + + const todo = Effect.fn("SessionHttpApi.todo")(function* (ctx: { params: { sessionID: SessionID } }) { + return yield* todoSvc.get(ctx.params.sessionID) + }) + + const diff = Effect.fn("SessionHttpApi.diff")(function* (ctx: { + params: { sessionID: SessionID } + query: typeof DiffQuery.Type + }) { + return yield* summary.diff({ sessionID: ctx.params.sessionID, messageID: ctx.query.messageID }) + }) + + const messages = Effect.fn("SessionHttpApi.messages")(function* (ctx: { + params: { sessionID: SessionID } + query: typeof MessagesQuery.Type + }) { + if (ctx.query.limit === undefined || ctx.query.limit === 0) { + yield* session.get(ctx.params.sessionID) + return yield* session.messages({ sessionID: ctx.params.sessionID }) + } + + const page = MessageV2.page({ + sessionID: ctx.params.sessionID, + limit: ctx.query.limit, + before: ctx.query.before, + }) + if (!page.cursor) return page.items + + const request = yield* HttpServerRequest.HttpServerRequest + const url = new URL(request.url, "http://localhost") + url.searchParams.set("limit", ctx.query.limit.toString()) + url.searchParams.set("before", page.cursor) + return HttpServerResponse.jsonUnsafe(page.items, { + headers: { + "Access-Control-Expose-Headers": "Link, X-Next-Cursor", + Link: `<${url.toString()}>; rel="next"`, + "X-Next-Cursor": page.cursor, + }, + }) + }) + + const message = Effect.fn("SessionHttpApi.message")(function* (ctx: { + params: { sessionID: SessionID; messageID: MessageID } + }) { + return yield* Effect.sync(() => + MessageV2.get({ sessionID: ctx.params.sessionID, messageID: ctx.params.messageID }), + ) + }) + + return HttpApiBuilder.group(SessionApi, "session", (handlers) => + handlers + .handle("list", list) + .handle("status", status) + .handle("get", get) + .handle("children", children) + .handle("todo", todo) + .handle("diff", diff) + .handle("messages", messages) + .handle("message", message), + ) + }), +).pipe( + Layer.provide(Session.defaultLayer), + Layer.provide(SessionStatus.defaultLayer), + Layer.provide(Todo.defaultLayer), + Layer.provide(SessionSummary.defaultLayer), +) diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index 57988b26f1..965f26b485 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -20,6 +20,7 @@ import { ExperimentalPaths } from "./httpapi/experimental" import { FilePaths } from "./httpapi/file" import { InstancePaths } from "./httpapi/instance" import { McpPaths } from "./httpapi/mcp" +import { SessionPaths } from "./httpapi/session" import { SyncPaths } from "./httpapi/sync" import { ProjectRoutes } from "./project" import { SessionRoutes } from "./session" @@ -93,6 +94,14 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.post(SyncPaths.start, (c) => handler(c.req.raw, context)) app.post(SyncPaths.replay, (c) => handler(c.req.raw, context)) app.post(SyncPaths.history, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.list, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.status, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.get, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.children, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.todo, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.diff, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.messages, (c) => handler(c.req.raw, context)) + app.get(SessionPaths.message, (c) => handler(c.req.raw, context)) } return app diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts new file mode 100644 index 0000000000..42cbb8495c --- /dev/null +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -0,0 +1,108 @@ +import { afterEach, describe, expect, test } from "bun:test" +import type { UpgradeWebSocket } from "hono/ws" +import { Effect } from "effect" +import { Flag } from "@opencode-ai/core/flag/flag" +import { ModelID, ProviderID } from "../../src/provider/schema" +import { Instance } from "../../src/project/instance" +import { InstanceRoutes } from "../../src/server/routes/instance" +import { SessionPaths } from "../../src/server/routes/instance/httpapi/session" +import { Session } from "../../src/session" +import { MessageID, PartID, type SessionID } from "../../src/session/schema" +import { MessageV2 } from "../../src/session/message-v2" +import { Log } from "../../src/util" +import { resetDatabase } from "../fixture/db" +import { tmpdir } from "../fixture/fixture" + +void Log.init({ print: false }) + +const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI +const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket + +function app() { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true + return InstanceRoutes(websocket) +} + +function runSession(fx: Effect.Effect) { + return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +} + +function pathFor(path: string, params: Record) { + return Object.entries(params).reduce((result, [key, value]) => result.replace(`:${key}`, value), path) +} + +async function createSession(directory: string, input?: Session.CreateInput) { + return Instance.provide({ + directory, + fn: async () => runSession(Session.Service.use((svc) => svc.create(input))), + }) +} + +async function createTextMessage(directory: string, sessionID: SessionID, text: string) { + return Instance.provide({ + directory, + fn: async () => + runSession( + Effect.gen(function* () { + const svc = yield* Session.Service + const info = yield* svc.updateMessage({ + id: MessageID.ascending(), + role: "user", + sessionID, + agent: "build", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + time: { created: Date.now() }, + }) + yield* svc.updatePart({ + id: PartID.ascending(), + sessionID, + messageID: info.id, + type: "text", + text, + }) + return info + }), + ), + }) +} + +async function json(response: Response) { + if (response.status !== 200) throw new Error(await response.text()) + return (await response.json()) as T +} + +afterEach(async () => { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original + await Instance.disposeAll() + await resetDatabase() +}) + +describe("session HttpApi", () => { + test("serves read routes through Hono bridge", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const headers = { "x-opencode-directory": tmp.path } + const parent = await createSession(tmp.path, { title: "parent" }) + const child = await createSession(tmp.path, { title: "child", parentID: parent.id }) + const message = await createTextMessage(tmp.path, parent.id, "hello") + await createTextMessage(tmp.path, parent.id, "world") + + expect((await json(await app().request(`${SessionPaths.list}?roots=true`, { headers }))).map((item) => item.id)).toContain(parent.id) + + expect(await json>(await app().request(SessionPaths.status, { headers }))).toEqual({}) + + expect(await json(await app().request(pathFor(SessionPaths.get, { sessionID: parent.id }), { headers }))).toMatchObject({ id: parent.id, title: "parent" }) + + expect((await json(await app().request(pathFor(SessionPaths.children, { sessionID: parent.id }), { headers }))).map((item) => item.id)).toEqual([child.id]) + + expect(await json(await app().request(pathFor(SessionPaths.todo, { sessionID: parent.id }), { headers }))).toEqual([]) + + expect(await json(await app().request(pathFor(SessionPaths.diff, { sessionID: parent.id }), { headers }))).toEqual([]) + + const messages = await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1`, { headers }) + const messagePage = await json(messages) + expect(messages.headers.get("x-next-cursor")).toBeTruthy() + expect(messagePage[0]?.parts[0]).toMatchObject({ type: "text" }) + + expect(await json(await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.id }), { headers }))).toMatchObject({ info: { id: message.id } }) + }) +}) From daff119fe453207cdb2c14878d87e407dbd05de0 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 15:50:09 +0000 Subject: [PATCH 18/34] chore: generate --- packages/opencode/specs/effect/http-api.md | 34 ++++++++--------- .../test/server/httpapi-session.test.ts | 38 +++++++++++++++---- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index fc17feb5b3..f6a0c06a5d 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -170,23 +170,23 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho ## Current Route Status -| Area | Status | Notes | -| ------------------------- | ----------------- | ---------------------------------------------------------------------------------------- | -| `question` | `bridged` | `GET /question`, reply, reject | -| `permission` | `bridged` | list and reply | -| `provider` | `bridged` | list, auth, OAuth authorize/callback | -| `config` | `bridged` | read, providers, update | -| `project` | `bridged` | list, current, git init, update | -| `file` | `bridged` partial | find text/file/symbol, list/content/status | -| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | -| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | -| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | -| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `bridged` partial | read routes; lifecycle, message mutations, streaming remain | -| `sync` | `bridged` | start/replay/history | -| `event` | `special` | SSE | -| `pty` | `special` | websocket | -| `tui` | `special` | UI bridge | +| Area | Status | Notes | +| ------------------------- | ----------------- | -------------------------------------------------------------------------- | +| `question` | `bridged` | `GET /question`, reply, reject | +| `permission` | `bridged` | list and reply | +| `provider` | `bridged` | list, auth, OAuth authorize/callback | +| `config` | `bridged` | read, providers, update | +| `project` | `bridged` | list, current, git init, update | +| `file` | `bridged` partial | find text/file/symbol, list/content/status | +| `mcp` | `bridged` | status, add, OAuth, connect/disconnect | +| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | +| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | +| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | +| `session` | `bridged` partial | read routes; lifecycle, message mutations, streaming remain | +| `sync` | `bridged` | start/replay/history | +| `event` | `special` | SSE | +| `pty` | `special` | websocket | +| `tui` | `special` | UI bridge | ## Full Route Checklist diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 42cbb8495c..4c4663d715 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -86,23 +86,47 @@ describe("session HttpApi", () => { const message = await createTextMessage(tmp.path, parent.id, "hello") await createTextMessage(tmp.path, parent.id, "world") - expect((await json(await app().request(`${SessionPaths.list}?roots=true`, { headers }))).map((item) => item.id)).toContain(parent.id) + expect( + (await json(await app().request(`${SessionPaths.list}?roots=true`, { headers }))).map( + (item) => item.id, + ), + ).toContain(parent.id) expect(await json>(await app().request(SessionPaths.status, { headers }))).toEqual({}) - expect(await json(await app().request(pathFor(SessionPaths.get, { sessionID: parent.id }), { headers }))).toMatchObject({ id: parent.id, title: "parent" }) + expect( + await json(await app().request(pathFor(SessionPaths.get, { sessionID: parent.id }), { headers })), + ).toMatchObject({ id: parent.id, title: "parent" }) - expect((await json(await app().request(pathFor(SessionPaths.children, { sessionID: parent.id }), { headers }))).map((item) => item.id)).toEqual([child.id]) + expect( + ( + await json( + await app().request(pathFor(SessionPaths.children, { sessionID: parent.id }), { headers }), + ) + ).map((item) => item.id), + ).toEqual([child.id]) - expect(await json(await app().request(pathFor(SessionPaths.todo, { sessionID: parent.id }), { headers }))).toEqual([]) + expect( + await json(await app().request(pathFor(SessionPaths.todo, { sessionID: parent.id }), { headers })), + ).toEqual([]) - expect(await json(await app().request(pathFor(SessionPaths.diff, { sessionID: parent.id }), { headers }))).toEqual([]) + expect( + await json(await app().request(pathFor(SessionPaths.diff, { sessionID: parent.id }), { headers })), + ).toEqual([]) - const messages = await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1`, { headers }) + const messages = await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1`, { + headers, + }) const messagePage = await json(messages) expect(messages.headers.get("x-next-cursor")).toBeTruthy() expect(messagePage[0]?.parts[0]).toMatchObject({ type: "text" }) - expect(await json(await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.id }), { headers }))).toMatchObject({ info: { id: message.id } }) + expect( + await json( + await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.id }), { + headers, + }), + ), + ).toMatchObject({ info: { id: message.id } }) }) }) From daaa2e591129bc00aac019afec6e8a93e2ab8f30 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 11:50:26 -0400 Subject: [PATCH 19/34] feat(httpapi): bridge session lifecycle routes (#24486) --- packages/opencode/specs/effect/http-api.md | 12 +- .../server/routes/instance/httpapi/session.ts | 160 +++++++++++++++++- .../src/server/routes/instance/index.ts | 5 + .../test/server/httpapi-session.test.ts | 44 +++++ 4 files changed, 214 insertions(+), 7 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index f6a0c06a5d..e11e88b7b4 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -291,12 +291,12 @@ This checklist tracks bridge parity only. Checked routes are available through t - [x] `GET /session/:sessionID` - get session. - [x] `GET /session/:sessionID/children` - get child sessions. - [x] `GET /session/:sessionID/todo` - get session todos. -- [ ] `POST /session` - create session. -- [ ] `DELETE /session/:sessionID` - delete session. -- [ ] `PATCH /session/:sessionID` - update session metadata. +- [x] `POST /session` - create session. +- [x] `DELETE /session/:sessionID` - delete session. +- [x] `PATCH /session/:sessionID` - update session metadata. - [ ] `POST /session/:sessionID/init` - run project init command. -- [ ] `POST /session/:sessionID/fork` - fork session. -- [ ] `POST /session/:sessionID/abort` - abort session. +- [x] `POST /session/:sessionID/fork` - fork session. +- [x] `POST /session/:sessionID/abort` - abort session. - [ ] `POST /session/:sessionID/share` - share session. - [x] `GET /session/:sessionID/diff` - session diff. - [ ] `DELETE /session/:sessionID/share` - unshare session. @@ -355,7 +355,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 6. [x] Bridge workspace create/remove/session-restore routes. 7. [x] Bridge sync start/replay/history routes. 8. [x] Bridge session read routes: list, status, get, children, todo, diff, messages. -9. [ ] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. +9. [x] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. 10. [ ] Bridge session share/summary/message/part mutation routes. 11. [ ] Replace event SSE with non-Hono Effect HTTP. 12. [ ] Replace pty websocket/control routes with non-Hono Effect HTTP. diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index e06c8d98ac..06f7d57910 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -1,7 +1,11 @@ import * as InstanceState from "@/effect/instance-state" +import { AppRuntime } from "@/effect/app-runtime" +import { Permission } from "@/permission" import { Instance } from "@/project/instance" +import { SessionShare } from "@/share" import { Session } from "@/session" import { MessageV2 } from "@/session/message-v2" +import { SessionPrompt } from "@/session/prompt" import { SessionStatus } from "@/session/status" import { SessionSummary } from "@/session/summary" import { Todo } from "@/session/todo" @@ -26,6 +30,18 @@ const MessagesQuery = Schema.Struct({ before: Schema.optional(Schema.String), }) const StatusMap = Schema.Record(Schema.String, SessionStatus.Info) +const UpdatePayload = Schema.Struct({ + title: Schema.optional(Schema.String), + permission: Schema.optional(Permission.Ruleset), + time: Schema.optional( + Schema.Struct({ + archived: Schema.optional(Schema.Number), + }), + ), +}).annotate({ identifier: "SessionUpdateInput" }) +const ForkPayload = Schema.Struct(Struct.omit(Session.ForkInput.fields, ["sessionID"])).annotate({ + identifier: "SessionForkInput", +}) export const SessionPaths = { list: root, @@ -36,6 +52,11 @@ export const SessionPaths = { diff: `${root}/:sessionID/diff`, messages: `${root}/:sessionID/message`, message: `${root}/:sessionID/message/:messageID`, + create: root, + remove: `${root}/:sessionID`, + update: `${root}/:sessionID`, + fork: `${root}/:sessionID/fork`, + abort: `${root}/:sessionID/abort`, } as const export const SessionApi = HttpApi.make("session") @@ -123,6 +144,58 @@ export const SessionApi = HttpApi.make("session") description: "Retrieve a specific message from a session by its message ID.", }), ), + HttpApiEndpoint.post("create", SessionPaths.create, { + payload: Session.CreateInput, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.create", + summary: "Create session", + description: "Create a new OpenCode session for interacting with AI assistants and managing conversations.", + }), + ), + HttpApiEndpoint.delete("remove", SessionPaths.remove, { + params: { sessionID: SessionID }, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.delete", + summary: "Delete session", + description: "Delete a session and permanently remove all associated data, including messages and history.", + }), + ), + HttpApiEndpoint.patch("update", SessionPaths.update, { + params: { sessionID: SessionID }, + payload: UpdatePayload, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.update", + summary: "Update session", + description: "Update properties of an existing session, such as title or other metadata.", + }), + ), + HttpApiEndpoint.post("fork", SessionPaths.fork, { + params: { sessionID: SessionID }, + payload: ForkPayload, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.fork", + summary: "Fork session", + description: "Create a new session by forking an existing session at a specific message point.", + }), + ), + HttpApiEndpoint.post("abort", SessionPaths.abort, { + params: { sessionID: SessionID }, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.abort", + summary: "Abort session", + description: "Abort an active session and stop any ongoing AI processing or command execution.", + }), + ), ) .annotateMerge( OpenApi.annotations({ @@ -222,6 +295,86 @@ export const sessionHandlers = Layer.unwrap( ) }) + const create = Effect.fn("SessionHttpApi.create")(function* (ctx: { payload: Session.CreateInput }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise(SessionShare.Service.use((svc) => svc.create(ctx.payload)).pipe(Effect.provide(SessionShare.defaultLayer))), + ), + ) + }) + + const remove = Effect.fn("SessionHttpApi.remove")(function* (ctx: { params: { sessionID: SessionID } }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise(Session.Service.use((svc) => svc.remove(ctx.params.sessionID)).pipe(Effect.provide(Session.defaultLayer))), + ), + ) + return true + }) + + const update = Effect.fn("SessionHttpApi.update")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof UpdatePayload.Type + }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Session.Service.use((svc) => + Effect.gen(function* () { + const current = yield* svc.get(ctx.params.sessionID) + if (ctx.payload.title !== undefined) { + yield* svc.setTitle({ sessionID: ctx.params.sessionID, title: ctx.payload.title }) + } + if (ctx.payload.permission !== undefined) { + yield* svc.setPermission({ + sessionID: ctx.params.sessionID, + permission: Permission.merge(current.permission ?? [], ctx.payload.permission), + }) + } + if (ctx.payload.time?.archived !== undefined) { + yield* svc.setArchived({ sessionID: ctx.params.sessionID, time: ctx.payload.time.archived }) + } + return yield* svc.get(ctx.params.sessionID) + }), + ).pipe(Effect.provide(Session.defaultLayer)), + ), + ), + ) + }) + + const fork = Effect.fn("SessionHttpApi.fork")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof ForkPayload.Type + }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Session.Service.use((svc) => svc.fork({ sessionID: ctx.params.sessionID, messageID: ctx.payload.messageID })).pipe( + Effect.provide(Session.defaultLayer), + ), + ), + ), + ) + }) + + const abort = Effect.fn("SessionHttpApi.abort")(function* (ctx: { params: { sessionID: SessionID } }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => svc.cancel(ctx.params.sessionID)).pipe( + Effect.provide(SessionPrompt.defaultLayer), + ), + ), + ), + ) + return true + }) + return HttpApiBuilder.group(SessionApi, "session", (handlers) => handlers .handle("list", list) @@ -231,7 +384,12 @@ export const sessionHandlers = Layer.unwrap( .handle("todo", todo) .handle("diff", diff) .handle("messages", messages) - .handle("message", message), + .handle("message", message) + .handle("create", create) + .handle("remove", remove) + .handle("update", update) + .handle("fork", fork) + .handle("abort", abort), ) }), ).pipe( diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index 965f26b485..ba029f0a3f 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -102,6 +102,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.get(SessionPaths.diff, (c) => handler(c.req.raw, context)) app.get(SessionPaths.messages, (c) => handler(c.req.raw, context)) app.get(SessionPaths.message, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.create, (c) => handler(c.req.raw, context)) + app.delete(SessionPaths.remove, (c) => handler(c.req.raw, context)) + app.patch(SessionPaths.update, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.fork, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.abort, (c) => handler(c.req.raw, context)) } return app diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 4c4663d715..d589a45a02 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -129,4 +129,48 @@ describe("session HttpApi", () => { ), ).toMatchObject({ info: { id: message.id } }) }) + + test("serves lifecycle mutation routes through Hono bridge", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false, share: "disabled" } }) + const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } + + const created = await json( + await app().request(SessionPaths.create, { + method: "POST", + headers, + body: JSON.stringify({ title: "created" }), + }), + ) + expect(created.title).toBe("created") + + const updated = await json( + await app().request(pathFor(SessionPaths.update, { sessionID: created.id }), { + method: "PATCH", + headers, + body: JSON.stringify({ title: "updated", time: { archived: 1 } }), + }), + ) + expect(updated).toMatchObject({ id: created.id, title: "updated", time: { archived: 1 } }) + + const forked = await json( + await app().request(pathFor(SessionPaths.fork, { sessionID: created.id }), { + method: "POST", + headers, + body: JSON.stringify({}), + }), + ) + expect(forked.id).not.toBe(created.id) + + expect( + await json( + await app().request(pathFor(SessionPaths.abort, { sessionID: created.id }), { method: "POST", headers }), + ), + ).toBe(true) + + expect( + await json( + await app().request(pathFor(SessionPaths.remove, { sessionID: created.id }), { method: "DELETE", headers }), + ), + ).toBe(true) + }) }) From 55adcdfd0764d6070e0b410731c91818b6f095c2 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 15:51:31 +0000 Subject: [PATCH 20/34] chore: generate --- .../src/server/routes/instance/httpapi/session.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index 06f7d57910..1db16c1135 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -299,7 +299,9 @@ export const sessionHandlers = Layer.unwrap( const instance = yield* InstanceState.context return yield* Effect.promise(() => Instance.restore(instance, () => - AppRuntime.runPromise(SessionShare.Service.use((svc) => svc.create(ctx.payload)).pipe(Effect.provide(SessionShare.defaultLayer))), + AppRuntime.runPromise( + SessionShare.Service.use((svc) => svc.create(ctx.payload)).pipe(Effect.provide(SessionShare.defaultLayer)), + ), ), ) }) @@ -308,7 +310,9 @@ export const sessionHandlers = Layer.unwrap( const instance = yield* InstanceState.context yield* Effect.promise(() => Instance.restore(instance, () => - AppRuntime.runPromise(Session.Service.use((svc) => svc.remove(ctx.params.sessionID)).pipe(Effect.provide(Session.defaultLayer))), + AppRuntime.runPromise( + Session.Service.use((svc) => svc.remove(ctx.params.sessionID)).pipe(Effect.provide(Session.defaultLayer)), + ), ), ) return true @@ -353,9 +357,9 @@ export const sessionHandlers = Layer.unwrap( return yield* Effect.promise(() => Instance.restore(instance, () => AppRuntime.runPromise( - Session.Service.use((svc) => svc.fork({ sessionID: ctx.params.sessionID, messageID: ctx.payload.messageID })).pipe( - Effect.provide(Session.defaultLayer), - ), + Session.Service.use((svc) => + svc.fork({ sessionID: ctx.params.sessionID, messageID: ctx.payload.messageID }), + ).pipe(Effect.provide(Session.defaultLayer)), ), ), ) From 151df05eeb83a014b5ba568e3e169ba2663e0439 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 12:00:02 -0400 Subject: [PATCH 21/34] feat(httpapi): bridge session message mutations (#24487) --- packages/opencode/specs/effect/http-api.md | 10 +- .../server/routes/instance/httpapi/session.ts | 150 +++++++++++++++++- .../src/server/routes/instance/index.ts | 5 + .../test/server/httpapi-session.test.ts | 54 ++++++- 4 files changed, 208 insertions(+), 11 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index e11e88b7b4..3ce4aa83af 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -297,15 +297,15 @@ This checklist tracks bridge parity only. Checked routes are available through t - [ ] `POST /session/:sessionID/init` - run project init command. - [x] `POST /session/:sessionID/fork` - fork session. - [x] `POST /session/:sessionID/abort` - abort session. -- [ ] `POST /session/:sessionID/share` - share session. +- [x] `POST /session/:sessionID/share` - share session. - [x] `GET /session/:sessionID/diff` - session diff. -- [ ] `DELETE /session/:sessionID/share` - unshare session. +- [x] `DELETE /session/:sessionID/share` - unshare session. - [ ] `POST /session/:sessionID/summarize` - summarize session. - [x] `GET /session/:sessionID/message` - list session messages. - [x] `GET /session/:sessionID/message/:messageID` - get message. -- [ ] `DELETE /session/:sessionID/message/:messageID` - delete message. -- [ ] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part. -- [ ] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part. +- [x] `DELETE /session/:sessionID/message/:messageID` - delete message. +- [x] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part. +- [x] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part. - [ ] `POST /session/:sessionID/message` - prompt with streaming response. - [ ] `POST /session/:sessionID/prompt_async` - async prompt. - [ ] `POST /session/:sessionID/command` - run command. diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index 1db16c1135..a028973d24 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -6,10 +6,11 @@ import { SessionShare } from "@/share" import { Session } from "@/session" import { MessageV2 } from "@/session/message-v2" import { SessionPrompt } from "@/session/prompt" +import { SessionRunState } from "@/session/run-state" import { SessionStatus } from "@/session/status" import { SessionSummary } from "@/session/summary" import { Todo } from "@/session/todo" -import { MessageID, SessionID } from "@/session/schema" +import { MessageID, PartID, SessionID } from "@/session/schema" import { Snapshot } from "@/snapshot" import { Effect, Layer, Schema, Struct } from "effect" import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http" @@ -57,6 +58,10 @@ export const SessionPaths = { update: `${root}/:sessionID`, fork: `${root}/:sessionID/fork`, abort: `${root}/:sessionID/abort`, + share: `${root}/:sessionID/share`, + deleteMessage: `${root}/:sessionID/message/:messageID`, + deletePart: `${root}/:sessionID/message/:messageID/part/:partID`, + updatePart: `${root}/:sessionID/message/:messageID/part/:partID`, } as const export const SessionApi = HttpApi.make("session") @@ -196,6 +201,56 @@ export const SessionApi = HttpApi.make("session") description: "Abort an active session and stop any ongoing AI processing or command execution.", }), ), + HttpApiEndpoint.post("share", SessionPaths.share, { + params: { sessionID: SessionID }, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.share", + summary: "Share session", + description: "Create a shareable link for a session, allowing others to view the conversation.", + }), + ), + HttpApiEndpoint.delete("unshare", SessionPaths.share, { + params: { sessionID: SessionID }, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.unshare", + summary: "Unshare session", + description: "Remove the shareable link for a session, making it private again.", + }), + ), + HttpApiEndpoint.delete("deleteMessage", SessionPaths.deleteMessage, { + params: { sessionID: SessionID, messageID: MessageID }, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.deleteMessage", + summary: "Delete message", + description: + "Permanently delete a specific message and all of its parts from a session without reverting file changes.", + }), + ), + HttpApiEndpoint.delete("deletePart", SessionPaths.deletePart, { + params: { sessionID: SessionID, messageID: MessageID, partID: PartID }, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "part.delete", + description: "Delete a part from a message.", + }), + ), + HttpApiEndpoint.patch("updatePart", SessionPaths.updatePart, { + params: { sessionID: SessionID, messageID: MessageID, partID: PartID }, + payload: MessageV2.Part, + success: MessageV2.Part, + }).annotateMerge( + OpenApi.annotations({ + identifier: "part.update", + description: "Update a part in a message.", + }), + ), ) .annotateMerge( OpenApi.annotations({ @@ -379,6 +434,91 @@ export const sessionHandlers = Layer.unwrap( return true }) + const share = Effect.fn("SessionHttpApi.share")(function* (ctx: { params: { sessionID: SessionID } }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Effect.gen(function* () { + const share = yield* SessionShare.Service + const session = yield* Session.Service + yield* share.share(ctx.params.sessionID) + return yield* session.get(ctx.params.sessionID) + }).pipe(Effect.provide(SessionShare.defaultLayer)), + ), + ), + ) + }) + + const unshare = Effect.fn("SessionHttpApi.unshare")(function* (ctx: { params: { sessionID: SessionID } }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Effect.gen(function* () { + const share = yield* SessionShare.Service + const session = yield* Session.Service + yield* share.unshare(ctx.params.sessionID) + return yield* session.get(ctx.params.sessionID) + }).pipe(Effect.provide(SessionShare.defaultLayer)), + ), + ), + ) + }) + + const deleteMessage = Effect.fn("SessionHttpApi.deleteMessage")(function* (ctx: { + params: { sessionID: SessionID; messageID: MessageID } + }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Effect.gen(function* () { + const state = yield* SessionRunState.Service + const session = yield* Session.Service + yield* state.assertNotBusy(ctx.params.sessionID) + yield* session.removeMessage(ctx.params) + }).pipe(Effect.provide(SessionRunState.defaultLayer), Effect.provide(Session.defaultLayer)), + ), + ), + ) + return true + }) + + const deletePart = Effect.fn("SessionHttpApi.deletePart")(function* (ctx: { + params: { sessionID: SessionID; messageID: MessageID; partID: PartID } + }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise(Session.Service.use((svc) => svc.removePart(ctx.params)).pipe(Effect.provide(Session.defaultLayer))), + ), + ) + return true + }) + + const updatePart = Effect.fn("SessionHttpApi.updatePart")(function* (ctx: { + params: { sessionID: SessionID; messageID: MessageID; partID: PartID } + payload: typeof MessageV2.Part.Type + }) { + const payload = MessageV2.Part.zod.parse(ctx.payload) + if ( + payload.id !== ctx.params.partID || + payload.messageID !== ctx.params.messageID || + payload.sessionID !== ctx.params.sessionID + ) { + throw new Error( + `Part mismatch: body.id='${payload.id}' vs partID='${ctx.params.partID}', body.messageID='${payload.messageID}' vs messageID='${ctx.params.messageID}', body.sessionID='${payload.sessionID}' vs sessionID='${ctx.params.sessionID}'`, + ) + } + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise(Session.Service.use((svc) => svc.updatePart(payload)).pipe(Effect.provide(Session.defaultLayer))), + ), + ) + }) + return HttpApiBuilder.group(SessionApi, "session", (handlers) => handlers .handle("list", list) @@ -393,11 +533,17 @@ export const sessionHandlers = Layer.unwrap( .handle("remove", remove) .handle("update", update) .handle("fork", fork) - .handle("abort", abort), + .handle("abort", abort) + .handle("share", share) + .handle("unshare", unshare) + .handle("deleteMessage", deleteMessage) + .handle("deletePart", deletePart) + .handle("updatePart", updatePart), ) }), ).pipe( Layer.provide(Session.defaultLayer), + Layer.provide(SessionRunState.defaultLayer), Layer.provide(SessionStatus.defaultLayer), Layer.provide(Todo.defaultLayer), Layer.provide(SessionSummary.defaultLayer), diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index ba029f0a3f..cbb46df227 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -107,6 +107,11 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.patch(SessionPaths.update, (c) => handler(c.req.raw, context)) app.post(SessionPaths.fork, (c) => handler(c.req.raw, context)) app.post(SessionPaths.abort, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.share, (c) => handler(c.req.raw, context)) + app.delete(SessionPaths.share, (c) => handler(c.req.raw, context)) + app.delete(SessionPaths.deleteMessage, (c) => handler(c.req.raw, context)) + app.delete(SessionPaths.deletePart, (c) => handler(c.req.raw, context)) + app.patch(SessionPaths.updatePart, (c) => handler(c.req.raw, context)) } return app diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index d589a45a02..7a1e8fd026 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -53,14 +53,14 @@ async function createTextMessage(directory: string, sessionID: SessionID, text: model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, time: { created: Date.now() }, }) - yield* svc.updatePart({ + const part = yield* svc.updatePart({ id: PartID.ascending(), sessionID, messageID: info.id, type: "text", text, }) - return info + return { info, part } }), ), }) @@ -123,11 +123,11 @@ describe("session HttpApi", () => { expect( await json( - await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.id }), { + await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.info.id }), { headers, }), ), - ).toMatchObject({ info: { id: message.id } }) + ).toMatchObject({ info: { id: message.info.id } }) }) test("serves lifecycle mutation routes through Hono bridge", async () => { @@ -173,4 +173,50 @@ describe("session HttpApi", () => { ), ).toBe(true) }) + + test("serves message mutation routes through Hono bridge", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } + const session = await createSession(tmp.path, { title: "messages" }) + const first = await createTextMessage(tmp.path, session.id, "first") + const second = await createTextMessage(tmp.path, session.id, "second") + + const updated = await json( + await app().request( + pathFor(SessionPaths.updatePart, { + sessionID: session.id, + messageID: first.info.id, + partID: first.part.id, + }), + { + method: "PATCH", + headers, + body: JSON.stringify({ ...first.part, text: "updated" }), + }, + ), + ) + expect(updated).toMatchObject({ id: first.part.id, type: "text", text: "updated" }) + + expect( + await json( + await app().request( + pathFor(SessionPaths.deletePart, { + sessionID: session.id, + messageID: first.info.id, + partID: first.part.id, + }), + { method: "DELETE", headers }, + ), + ), + ).toBe(true) + + expect( + await json( + await app().request(pathFor(SessionPaths.deleteMessage, { sessionID: session.id, messageID: second.info.id }), { + method: "DELETE", + headers, + }), + ), + ).toBe(true) + }) }) From 301ecb185e06a230c6d720845b04effa84450976 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 16:01:07 +0000 Subject: [PATCH 22/34] chore: generate --- .../src/server/routes/instance/httpapi/session.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index a028973d24..b38740d93f 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -491,7 +491,9 @@ export const sessionHandlers = Layer.unwrap( const instance = yield* InstanceState.context yield* Effect.promise(() => Instance.restore(instance, () => - AppRuntime.runPromise(Session.Service.use((svc) => svc.removePart(ctx.params)).pipe(Effect.provide(Session.defaultLayer))), + AppRuntime.runPromise( + Session.Service.use((svc) => svc.removePart(ctx.params)).pipe(Effect.provide(Session.defaultLayer)), + ), ), ) return true @@ -514,7 +516,9 @@ export const sessionHandlers = Layer.unwrap( const instance = yield* InstanceState.context return yield* Effect.promise(() => Instance.restore(instance, () => - AppRuntime.runPromise(Session.Service.use((svc) => svc.updatePart(payload)).pipe(Effect.provide(Session.defaultLayer))), + AppRuntime.runPromise( + Session.Service.use((svc) => svc.updatePart(payload)).pipe(Effect.provide(Session.defaultLayer)), + ), ), ) }) From c5b67927afd2a86945fbcbce42d3cfc2eba047a8 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 12:24:19 -0400 Subject: [PATCH 23/34] feat(httpapi): bridge remaining session routes (#24510) --- packages/opencode/specs/effect/http-api.md | 22 +- .../server/routes/instance/httpapi/session.ts | 366 +++++++++++++++++- .../src/server/routes/instance/index.ts | 9 + .../test/server/httpapi-session.test.ts | 60 ++- 4 files changed, 443 insertions(+), 14 deletions(-) diff --git a/packages/opencode/specs/effect/http-api.md b/packages/opencode/specs/effect/http-api.md index 3ce4aa83af..5f16ef197e 100644 --- a/packages/opencode/specs/effect/http-api.md +++ b/packages/opencode/specs/effect/http-api.md @@ -182,7 +182,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho | `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore | | top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose | | experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list | -| `session` | `bridged` partial | read routes; lifecycle, message mutations, streaming remain | +| `session` | `bridged` | read, lifecycle, prompt, message/part mutations, revert, permission reply | | `sync` | `bridged` | start/replay/history | | `event` | `special` | SSE | | `pty` | `special` | websocket | @@ -294,25 +294,25 @@ This checklist tracks bridge parity only. Checked routes are available through t - [x] `POST /session` - create session. - [x] `DELETE /session/:sessionID` - delete session. - [x] `PATCH /session/:sessionID` - update session metadata. -- [ ] `POST /session/:sessionID/init` - run project init command. +- [x] `POST /session/:sessionID/init` - run project init command. - [x] `POST /session/:sessionID/fork` - fork session. - [x] `POST /session/:sessionID/abort` - abort session. - [x] `POST /session/:sessionID/share` - share session. - [x] `GET /session/:sessionID/diff` - session diff. - [x] `DELETE /session/:sessionID/share` - unshare session. -- [ ] `POST /session/:sessionID/summarize` - summarize session. +- [x] `POST /session/:sessionID/summarize` - summarize session. - [x] `GET /session/:sessionID/message` - list session messages. - [x] `GET /session/:sessionID/message/:messageID` - get message. - [x] `DELETE /session/:sessionID/message/:messageID` - delete message. - [x] `DELETE /session/:sessionID/message/:messageID/part/:partID` - delete part. - [x] `PATCH /session/:sessionID/message/:messageID/part/:partID` - update part. -- [ ] `POST /session/:sessionID/message` - prompt with streaming response. -- [ ] `POST /session/:sessionID/prompt_async` - async prompt. -- [ ] `POST /session/:sessionID/command` - run command. -- [ ] `POST /session/:sessionID/shell` - run shell command. -- [ ] `POST /session/:sessionID/revert` - revert message. -- [ ] `POST /session/:sessionID/unrevert` - restore reverted messages. -- [ ] `POST /session/:sessionID/permissions/:permissionID` - deprecated permission response route. +- [x] `POST /session/:sessionID/message` - prompt with streaming response. +- [x] `POST /session/:sessionID/prompt_async` - async prompt. +- [x] `POST /session/:sessionID/command` - run command. +- [x] `POST /session/:sessionID/shell` - run shell command. +- [x] `POST /session/:sessionID/revert` - revert message. +- [x] `POST /session/:sessionID/unrevert` - restore reverted messages. +- [x] `POST /session/:sessionID/permissions/:permissionID` - deprecated permission response route. ### Event Routes @@ -356,7 +356,7 @@ Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays rev 7. [x] Bridge sync start/replay/history routes. 8. [x] Bridge session read routes: list, status, get, children, todo, diff, messages. 9. [x] Bridge session lifecycle mutation routes: create, delete, update, fork, abort. -10. [ ] Bridge session share/summary/message/part mutation routes. +10. [x] Bridge remaining session mutation and prompt routes. 11. [ ] Replace event SSE with non-Hono Effect HTTP. 12. [ ] Replace pty websocket/control routes with non-Hono Effect HTTP. 13. [ ] Replace tui bridge routes or explicitly isolate them behind a non-Hono compatibility layer. diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index b38740d93f..36645fd7eb 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -1,22 +1,41 @@ import * as InstanceState from "@/effect/instance-state" import { AppRuntime } from "@/effect/app-runtime" +import { Agent } from "@/agent/agent" +import { Bus } from "@/bus" +import { Command } from "@/command" import { Permission } from "@/permission" +import { PermissionID } from "@/permission/schema" import { Instance } from "@/project/instance" +import { ModelID, ProviderID } from "@/provider/schema" import { SessionShare } from "@/share" import { Session } from "@/session" +import { SessionCompaction } from "@/session/compaction" import { MessageV2 } from "@/session/message-v2" import { SessionPrompt } from "@/session/prompt" +import { SessionRevert } from "@/session/revert" import { SessionRunState } from "@/session/run-state" import { SessionStatus } from "@/session/status" import { SessionSummary } from "@/session/summary" import { Todo } from "@/session/todo" import { MessageID, PartID, SessionID } from "@/session/schema" import { Snapshot } from "@/snapshot" +import { Log } from "@/util" +import { NamedError } from "@opencode-ai/core/util/error" import { Effect, Layer, Schema, Struct } from "effect" +import * as Stream from "effect/Stream" import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http" -import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { + HttpApi, + HttpApiBuilder, + HttpApiEndpoint, + HttpApiError, + HttpApiGroup, + HttpApiSchema, + OpenApi, +} from "effect/unstable/httpapi" import { Authorization } from "./auth" +const log = Log.create({ service: "server" }) const root = "/session" const ListQuery = Schema.Struct({ directory: Schema.optional(Schema.String), @@ -43,6 +62,31 @@ const UpdatePayload = Schema.Struct({ const ForkPayload = Schema.Struct(Struct.omit(Session.ForkInput.fields, ["sessionID"])).annotate({ identifier: "SessionForkInput", }) +const InitPayload = Schema.Struct({ + modelID: ModelID, + providerID: ProviderID, + messageID: MessageID, +}).annotate({ identifier: "SessionInitInput" }) +const SummarizePayload = Schema.Struct({ + providerID: ProviderID, + modelID: ModelID, + auto: Schema.optional(Schema.Boolean), +}).annotate({ identifier: "SessionSummarizeInput" }) +const PromptPayload = Schema.Struct(Struct.omit(SessionPrompt.PromptInput.fields, ["sessionID"])).annotate({ + identifier: "SessionPromptInput", +}) +const CommandPayload = Schema.Struct(Struct.omit(SessionPrompt.CommandInput.fields, ["sessionID"])).annotate({ + identifier: "SessionCommandInput", +}) +const ShellPayload = Schema.Struct(Struct.omit(SessionPrompt.ShellInput.fields, ["sessionID"])).annotate({ + identifier: "SessionShellInput", +}) +const RevertPayload = Schema.Struct(Struct.omit(SessionRevert.RevertInput.fields, ["sessionID"])).annotate({ + identifier: "SessionRevertInput", +}) +const PermissionResponsePayload = Schema.Struct({ + response: Permission.Reply, +}).annotate({ identifier: "SessionPermissionResponseInput" }) export const SessionPaths = { list: root, @@ -59,6 +103,15 @@ export const SessionPaths = { fork: `${root}/:sessionID/fork`, abort: `${root}/:sessionID/abort`, share: `${root}/:sessionID/share`, + init: `${root}/:sessionID/init`, + summarize: `${root}/:sessionID/summarize`, + prompt: `${root}/:sessionID/message`, + promptAsync: `${root}/:sessionID/prompt_async`, + command: `${root}/:sessionID/command`, + shell: `${root}/:sessionID/shell`, + revert: `${root}/:sessionID/revert`, + unrevert: `${root}/:sessionID/unrevert`, + permissions: `${root}/:sessionID/permissions/:permissionID`, deleteMessage: `${root}/:sessionID/message/:messageID`, deletePart: `${root}/:sessionID/message/:messageID/part/:partID`, updatePart: `${root}/:sessionID/message/:messageID/part/:partID`, @@ -201,6 +254,18 @@ export const SessionApi = HttpApi.make("session") description: "Abort an active session and stop any ongoing AI processing or command execution.", }), ), + HttpApiEndpoint.post("init", SessionPaths.init, { + params: { sessionID: SessionID }, + payload: InitPayload, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.init", + summary: "Initialize session", + description: + "Analyze the current application and create an AGENTS.md file with project-specific agent configurations.", + }), + ), HttpApiEndpoint.post("share", SessionPaths.share, { params: { sessionID: SessionID }, success: Session.Info, @@ -221,6 +286,95 @@ export const SessionApi = HttpApi.make("session") description: "Remove the shareable link for a session, making it private again.", }), ), + HttpApiEndpoint.post("summarize", SessionPaths.summarize, { + params: { sessionID: SessionID }, + payload: SummarizePayload, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.summarize", + summary: "Summarize session", + description: "Generate a concise summary of the session using AI compaction to preserve key information.", + }), + ), + HttpApiEndpoint.post("prompt", SessionPaths.prompt, { + params: { sessionID: SessionID }, + payload: PromptPayload, + success: MessageV2.WithParts, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.prompt", + summary: "Send message", + description: "Create and send a new message to a session, streaming the AI response.", + }), + ), + HttpApiEndpoint.post("promptAsync", SessionPaths.promptAsync, { + params: { sessionID: SessionID }, + payload: PromptPayload, + success: HttpApiSchema.NoContent, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.prompt_async", + summary: "Send async message", + description: + "Create and send a new message to a session asynchronously, starting the session if needed and returning immediately.", + }), + ), + HttpApiEndpoint.post("command", SessionPaths.command, { + params: { sessionID: SessionID }, + payload: CommandPayload, + success: MessageV2.WithParts, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.command", + summary: "Send command", + description: "Send a new command to a session for execution by the AI assistant.", + }), + ), + HttpApiEndpoint.post("shell", SessionPaths.shell, { + params: { sessionID: SessionID }, + payload: ShellPayload, + success: MessageV2.WithParts, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.shell", + summary: "Run shell command", + description: "Execute a shell command within the session context and return the AI's response.", + }), + ), + HttpApiEndpoint.post("revert", SessionPaths.revert, { + params: { sessionID: SessionID }, + payload: RevertPayload, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.revert", + summary: "Revert message", + description: "Revert a specific message in a session, undoing its effects and restoring the previous state.", + }), + ), + HttpApiEndpoint.post("unrevert", SessionPaths.unrevert, { + params: { sessionID: SessionID }, + success: Session.Info, + }).annotateMerge( + OpenApi.annotations({ + identifier: "session.unrevert", + summary: "Restore reverted messages", + description: "Restore all previously reverted messages in a session.", + }), + ), + HttpApiEndpoint.post("permissionRespond", SessionPaths.permissions, { + params: { sessionID: SessionID, permissionID: PermissionID }, + payload: PermissionResponsePayload, + success: Schema.Boolean, + }).annotateMerge( + OpenApi.annotations({ + identifier: "permission.respond", + summary: "Respond to permission", + description: "Approve or deny a permission request from the AI assistant.", + deprecated: true, + }), + ), HttpApiEndpoint.delete("deleteMessage", SessionPaths.deleteMessage, { params: { sessionID: SessionID, messageID: MessageID }, success: Schema.Boolean, @@ -317,6 +471,14 @@ export const sessionHandlers = Layer.unwrap( params: { sessionID: SessionID } query: typeof MessagesQuery.Type }) { + if (ctx.query.before !== undefined && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({}) + if (ctx.query.before !== undefined) { + const before = ctx.query.before + yield* Effect.try({ + try: () => MessageV2.cursor.decode(before), + catch: () => new HttpApiError.BadRequest({}), + }) + } if (ctx.query.limit === undefined || ctx.query.limit === 0) { yield* session.get(ctx.params.sessionID) return yield* session.messages({ sessionID: ctx.params.sessionID }) @@ -434,6 +596,29 @@ export const sessionHandlers = Layer.unwrap( return true }) + const init = Effect.fn("SessionHttpApi.init")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof InitPayload.Type + }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => + svc.command({ + sessionID: ctx.params.sessionID, + messageID: ctx.payload.messageID, + model: `${ctx.payload.providerID}/${ctx.payload.modelID}`, + command: Command.Default.INIT, + arguments: "", + }), + ).pipe(Effect.provide(SessionPrompt.defaultLayer)), + ), + ), + ) + return true + }) + const share = Effect.fn("SessionHttpApi.share")(function* (ctx: { params: { sessionID: SessionID } }) { const instance = yield* InstanceState.context return yield* Effect.promise(() => @@ -466,6 +651,174 @@ export const sessionHandlers = Layer.unwrap( ) }) + const summarize = Effect.fn("SessionHttpApi.summarize")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof SummarizePayload.Type + }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Effect.gen(function* () { + const session = yield* Session.Service + const revert = yield* SessionRevert.Service + const compact = yield* SessionCompaction.Service + const prompt = yield* SessionPrompt.Service + const agent = yield* Agent.Service + + yield* revert.cleanup(yield* session.get(ctx.params.sessionID)) + const messages = yield* session.messages({ sessionID: ctx.params.sessionID }) + const defaultAgent = yield* agent.defaultAgent() + const currentAgent = messages.findLast((message) => message.info.role === "user")?.info.agent ?? defaultAgent + + yield* compact.create({ + sessionID: ctx.params.sessionID, + agent: currentAgent, + model: { + providerID: ctx.payload.providerID, + modelID: ctx.payload.modelID, + }, + auto: ctx.payload.auto ?? false, + }) + yield* prompt.loop({ sessionID: ctx.params.sessionID }) + }).pipe( + Effect.provide(SessionRevert.defaultLayer), + Effect.provide(SessionCompaction.defaultLayer), + Effect.provide(SessionPrompt.defaultLayer), + Effect.provide(Agent.defaultLayer), + Effect.provide(Session.defaultLayer), + ), + ), + ), + ) + return true + }) + + const prompt = Effect.fn("SessionHttpApi.prompt")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof PromptPayload.Type + }) { + const instance = yield* InstanceState.context + return HttpServerResponse.stream( + Stream.fromEffect( + Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => + svc.prompt({ ...ctx.payload, sessionID: ctx.params.sessionID } as unknown as SessionPrompt.PromptInput), + ).pipe(Effect.provide(SessionPrompt.defaultLayer)), + ), + ), + ), + ).pipe(Stream.map((message) => JSON.stringify(message)), Stream.encodeText), + { contentType: "application/json" }, + ) + }) + + const promptAsync = Effect.fn("SessionHttpApi.promptAsync")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof PromptPayload.Type + }) { + const instance = yield* InstanceState.context + yield* Effect.sync(() => { + Instance.restore(instance, () => { + void AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => + svc.prompt({ ...ctx.payload, sessionID: ctx.params.sessionID } as unknown as SessionPrompt.PromptInput), + ).pipe(Effect.provide(SessionPrompt.defaultLayer)), + ).catch((error) => { + log.error("prompt_async failed", { sessionID: ctx.params.sessionID, error }) + void Bus.publish(Session.Event.Error, { + sessionID: ctx.params.sessionID, + error: new NamedError.Unknown({ + message: error instanceof Error ? error.message : String(error), + }).toObject(), + }) + }) + }) + }) + return HttpApiSchema.NoContent.make() + }) + + const command = Effect.fn("SessionHttpApi.command")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof CommandPayload.Type + }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => + svc.command({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.CommandInput), + ).pipe(Effect.provide(SessionPrompt.defaultLayer)), + ), + ), + ) + }) + + const shell = Effect.fn("SessionHttpApi.shell")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof ShellPayload.Type + }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionPrompt.Service.use((svc) => + svc.shell({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.ShellInput), + ).pipe(Effect.provide(SessionPrompt.defaultLayer)), + ), + ), + ) + }) + + const revert = Effect.fn("SessionHttpApi.revert")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof RevertPayload.Type + }) { + const instance = yield* InstanceState.context + log.info("revert", ctx.payload) + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionRevert.Service.use((svc) => + svc.revert({ sessionID: ctx.params.sessionID, ...ctx.payload }), + ).pipe(Effect.provide(SessionRevert.defaultLayer)), + ), + ), + ) + }) + + const unrevert = Effect.fn("SessionHttpApi.unrevert")(function* (ctx: { params: { sessionID: SessionID } }) { + const instance = yield* InstanceState.context + return yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + SessionRevert.Service.use((svc) => svc.unrevert({ sessionID: ctx.params.sessionID })).pipe( + Effect.provide(SessionRevert.defaultLayer), + ), + ), + ), + ) + }) + + const permissionRespond = Effect.fn("SessionHttpApi.permissionRespond")(function* (ctx: { + params: { permissionID: PermissionID } + payload: typeof PermissionResponsePayload.Type + }) { + const instance = yield* InstanceState.context + yield* Effect.promise(() => + Instance.restore(instance, () => + AppRuntime.runPromise( + Permission.Service.use((svc) => + svc.reply({ requestID: ctx.params.permissionID, reply: ctx.payload.response }), + ).pipe(Effect.provide(Permission.defaultLayer)), + ), + ), + ) + return true + }) + const deleteMessage = Effect.fn("SessionHttpApi.deleteMessage")(function* (ctx: { params: { sessionID: SessionID; messageID: MessageID } }) { @@ -503,7 +856,7 @@ export const sessionHandlers = Layer.unwrap( params: { sessionID: SessionID; messageID: MessageID; partID: PartID } payload: typeof MessageV2.Part.Type }) { - const payload = MessageV2.Part.zod.parse(ctx.payload) + const payload = ctx.payload as MessageV2.Part if ( payload.id !== ctx.params.partID || payload.messageID !== ctx.params.messageID || @@ -538,8 +891,17 @@ export const sessionHandlers = Layer.unwrap( .handle("update", update) .handle("fork", fork) .handle("abort", abort) + .handle("init", init) .handle("share", share) .handle("unshare", unshare) + .handle("summarize", summarize) + .handle("prompt", prompt) + .handle("promptAsync", promptAsync) + .handle("command", command) + .handle("shell", shell) + .handle("revert", revert) + .handle("unrevert", unrevert) + .handle("permissionRespond", permissionRespond) .handle("deleteMessage", deleteMessage) .handle("deletePart", deletePart) .handle("updatePart", updatePart), diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts index cbb46df227..4c0503af5a 100644 --- a/packages/opencode/src/server/routes/instance/index.ts +++ b/packages/opencode/src/server/routes/instance/index.ts @@ -105,10 +105,19 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => { app.post(SessionPaths.create, (c) => handler(c.req.raw, context)) app.delete(SessionPaths.remove, (c) => handler(c.req.raw, context)) app.patch(SessionPaths.update, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.init, (c) => handler(c.req.raw, context)) app.post(SessionPaths.fork, (c) => handler(c.req.raw, context)) app.post(SessionPaths.abort, (c) => handler(c.req.raw, context)) app.post(SessionPaths.share, (c) => handler(c.req.raw, context)) app.delete(SessionPaths.share, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.summarize, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.prompt, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.promptAsync, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.command, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.shell, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.revert, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.unrevert, (c) => handler(c.req.raw, context)) + app.post(SessionPaths.permissions, (c) => handler(c.req.raw, context)) app.delete(SessionPaths.deleteMessage, (c) => handler(c.req.raw, context)) app.delete(SessionPaths.deletePart, (c) => handler(c.req.raw, context)) app.patch(SessionPaths.updatePart, (c) => handler(c.req.raw, context)) diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 7a1e8fd026..e6c0919820 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test" import type { UpgradeWebSocket } from "hono/ws" import { Effect } from "effect" import { Flag } from "@opencode-ai/core/flag/flag" +import { PermissionID } from "../../src/permission/schema" import { ModelID, ProviderID } from "../../src/provider/schema" import { Instance } from "../../src/project/instance" import { InstanceRoutes } from "../../src/server/routes/instance" @@ -118,9 +119,25 @@ describe("session HttpApi", () => { headers, }) const messagePage = await json(messages) - expect(messages.headers.get("x-next-cursor")).toBeTruthy() + const nextCursor = messages.headers.get("x-next-cursor") + expect(nextCursor).toBeTruthy() expect(messagePage[0]?.parts[0]).toMatchObject({ type: "text" }) + expect( + ( + await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?before=${nextCursor}`, { + headers, + }) + ).status, + ).toBe(400) + expect( + ( + await app().request(`${pathFor(SessionPaths.messages, { sessionID: parent.id })}?limit=1&before=invalid`, { + headers, + }) + ).status, + ).toBe(400) + expect( await json( await app().request(pathFor(SessionPaths.message, { sessionID: parent.id, messageID: message.info.id }), { @@ -219,4 +236,45 @@ describe("session HttpApi", () => { ), ).toBe(true) }) + + test("serves remaining non-LLM session mutation routes through Hono bridge", async () => { + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) + const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" } + const session = await createSession(tmp.path, { title: "remaining" }) + + expect( + await json( + await app().request(pathFor(SessionPaths.revert, { sessionID: session.id }), { + method: "POST", + headers, + body: JSON.stringify({ messageID: MessageID.ascending() }), + }), + ), + ).toMatchObject({ id: session.id }) + + expect( + await json( + await app().request(pathFor(SessionPaths.unrevert, { sessionID: session.id }), { + method: "POST", + headers, + }), + ), + ).toMatchObject({ id: session.id }) + + expect( + await json( + await app().request( + pathFor(SessionPaths.permissions, { + sessionID: session.id, + permissionID: String(PermissionID.ascending()), + }), + { + method: "POST", + headers, + body: JSON.stringify({ response: "once" }), + }, + ), + ), + ).toBe(true) + }) }) From 41f5e8a8616b22862f181c67f30c5420ac2e47af Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 16:25:19 +0000 Subject: [PATCH 24/34] chore: generate --- .../server/routes/instance/httpapi/session.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/session.ts b/packages/opencode/src/server/routes/instance/httpapi/session.ts index 36645fd7eb..08bf424f4a 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/session.ts @@ -350,7 +350,8 @@ export const SessionApi = HttpApi.make("session") OpenApi.annotations({ identifier: "session.revert", summary: "Revert message", - description: "Revert a specific message in a session, undoing its effects and restoring the previous state.", + description: + "Revert a specific message in a session, undoing its effects and restoring the previous state.", }), ), HttpApiEndpoint.post("unrevert", SessionPaths.unrevert, { @@ -669,7 +670,8 @@ export const sessionHandlers = Layer.unwrap( yield* revert.cleanup(yield* session.get(ctx.params.sessionID)) const messages = yield* session.messages({ sessionID: ctx.params.sessionID }) const defaultAgent = yield* agent.defaultAgent() - const currentAgent = messages.findLast((message) => message.info.role === "user")?.info.agent ?? defaultAgent + const currentAgent = + messages.findLast((message) => message.info.role === "user")?.info.agent ?? defaultAgent yield* compact.create({ sessionID: ctx.params.sessionID, @@ -705,12 +707,18 @@ export const sessionHandlers = Layer.unwrap( Instance.restore(instance, () => AppRuntime.runPromise( SessionPrompt.Service.use((svc) => - svc.prompt({ ...ctx.payload, sessionID: ctx.params.sessionID } as unknown as SessionPrompt.PromptInput), + svc.prompt({ + ...ctx.payload, + sessionID: ctx.params.sessionID, + } as unknown as SessionPrompt.PromptInput), ).pipe(Effect.provide(SessionPrompt.defaultLayer)), ), ), ), - ).pipe(Stream.map((message) => JSON.stringify(message)), Stream.encodeText), + ).pipe( + Stream.map((message) => JSON.stringify(message)), + Stream.encodeText, + ), { contentType: "application/json" }, ) }) @@ -781,9 +789,9 @@ export const sessionHandlers = Layer.unwrap( return yield* Effect.promise(() => Instance.restore(instance, () => AppRuntime.runPromise( - SessionRevert.Service.use((svc) => - svc.revert({ sessionID: ctx.params.sessionID, ...ctx.payload }), - ).pipe(Effect.provide(SessionRevert.defaultLayer)), + SessionRevert.Service.use((svc) => svc.revert({ sessionID: ctx.params.sessionID, ...ctx.payload })).pipe( + Effect.provide(SessionRevert.defaultLayer), + ), ), ), ) From 79c66e353f718684f6e9da01c2aa2444cebcaf53 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Apr 2026 12:50:01 -0400 Subject: [PATCH 25/34] sync --- .opencode/agent/translator.md | 899 -------------------- .opencode/command/translate.md | 14 + packages/web/src/content/docs/ar/zen.mdx | 27 +- packages/web/src/content/docs/bs/zen.mdx | 27 +- packages/web/src/content/docs/da/zen.mdx | 27 +- packages/web/src/content/docs/de/zen.mdx | 27 +- packages/web/src/content/docs/es/zen.mdx | 27 +- packages/web/src/content/docs/fr/zen.mdx | 27 +- packages/web/src/content/docs/it/zen.mdx | 27 +- packages/web/src/content/docs/ja/zen.mdx | 27 +- packages/web/src/content/docs/ko/zen.mdx | 27 +- packages/web/src/content/docs/nb/zen.mdx | 27 +- packages/web/src/content/docs/pl/zen.mdx | 29 +- packages/web/src/content/docs/pt-br/zen.mdx | 27 +- packages/web/src/content/docs/ru/zen.mdx | 27 +- packages/web/src/content/docs/th/zen.mdx | 29 +- packages/web/src/content/docs/tr/zen.mdx | 27 +- packages/web/src/content/docs/zen.mdx | 27 +- packages/web/src/content/docs/zh-cn/zen.mdx | 27 +- packages/web/src/content/docs/zh-tw/zen.mdx | 27 +- 20 files changed, 322 insertions(+), 1081 deletions(-) delete mode 100644 .opencode/agent/translator.md create mode 100644 .opencode/command/translate.md diff --git a/.opencode/agent/translator.md b/.opencode/agent/translator.md deleted file mode 100644 index 8ac7025f17..0000000000 --- a/.opencode/agent/translator.md +++ /dev/null @@ -1,899 +0,0 @@ ---- -description: Translate content for a specified locale while preserving technical terms -mode: subagent -model: opencode/gpt-5.4 ---- - -You are a professional translator and localization specialist. - -Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE). - -Requirements: - -- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure). -- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks. -- Also preserve every term listed in the Do-Not-Translate glossary below. -- Also apply locale-specific guidance from `.opencode/glossary/.md` when available (for example, `zh-cn.md`). -- Do not modify fenced code blocks. -- Output ONLY the translation (no commentary). - -If the target locale is missing, ask the user to provide it. -If no locale-specific glossary exists, use the global glossary only. - ---- - -# Locale-Specific Glossaries - -When a locale glossary exists, use it to: - -- Apply preferred wording for recurring UI/docs terms in that locale -- Preserve locale-specific do-not-translate terms and casing decisions -- Prefer natural phrasing over literal translation when the locale file calls it out -- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo) - -Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below. - ---- - -# Do-Not-Translate Terms (OpenCode Docs) - -Generated from: `packages/web/src/content/docs/*.mdx` (default English docs) -Generated on: 2026-02-10 - -Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation). - -General rules (verbatim, even if not listed below): - -- Anything inside inline code (single backticks) or fenced code blocks (triple backticks) -- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers -- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars - -## Proper nouns and product names - -Additional (not reliably captured via link text): - -```text -Astro -Bun -Chocolatey -Cursor -Docker -Git -GitHub Actions -GitLab CI -GNOME Terminal -Homebrew -Mise -Neovim -Node.js -npm -Obsidian -opencode -opencode-ai -Paru -pnpm -ripgrep -Scoop -SST -Starlight -Visual Studio Code -VS Code -VSCodium -Windsurf -Windows Terminal -Yarn -Zellij -Zed -anomalyco -``` - -Extracted from link labels in the English docs (review and prune as desired): - -```text -@openspoon/subtask2 -302.AI console -ACP progress report -Agent Client Protocol -Agent Skills -Agentic -AGENTS.md -AI SDK -Alacritty -Anthropic -Anthropic's Data Policies -Atom One -Avante.nvim -Ayu -Azure AI Foundry -Azure portal -Baseten -built-in GITHUB_TOKEN -Bun.$ -Catppuccin -Cerebras console -ChatGPT Plus or Pro -Cloudflare dashboard -CodeCompanion.nvim -CodeNomad -Configuring Adapters: Environment Variables -Context7 MCP server -Cortecs console -Deep Infra dashboard -DeepSeek console -Duo Agent Platform -Everforest -Fireworks AI console -Firmware dashboard -Ghostty -GitLab CLI agents docs -GitLab docs -GitLab User Settings > Access Tokens -Granular Rules (Object Syntax) -Grep by Vercel -Groq console -Gruvbox -Helicone -Helicone documentation -Helicone Header Directory -Helicone's Model Directory -Hugging Face Inference Providers -Hugging Face settings -install WSL -IO.NET console -JetBrains IDE -Kanagawa -Kitty -MiniMax API Console -Models.dev -Moonshot AI console -Nebius Token Factory console -Nord -OAuth -Ollama integration docs -OpenAI's Data Policies -OpenChamber -OpenCode -OpenCode config -OpenCode Config -OpenCode TUI with the opencode theme -OpenCode Web - Active Session -OpenCode Web - New Session -OpenCode Web - See Servers -OpenCode Zen -OpenCode-Obsidian -OpenRouter dashboard -OpenWork -OVHcloud panel -Pro+ subscription -SAP BTP Cockpit -Scaleway Console IAM settings -Scaleway Generative APIs -SDK documentation -Sentry MCP server -shell API -Together AI console -Tokyonight -Unified Billing -Venice AI console -Vercel dashboard -WezTerm -Windows Subsystem for Linux (WSL) -WSL -WSL (Windows Subsystem for Linux) -WSL extension -xAI console -Z.AI API console -Zed -ZenMux dashboard -Zod -``` - -## Acronyms and initialisms - -```text -ACP -AGENTS -AI -AI21 -ANSI -API -AST -AWS -BTP -CD -CDN -CI -CLI -CMD -CORS -DEBUG -EKS -ERROR -FAQ -GLM -GNOME -GPT -HTML -HTTP -HTTPS -IAM -ID -IDE -INFO -IO -IP -IRSA -JS -JSON -JSONC -K2 -LLM -LM -LSP -M2 -MCP -MR -NET -NPM -NTLM -OIDC -OS -PAT -PATH -PHP -PR -PTY -README -RFC -RPC -SAP -SDK -SKILL -SSE -SSO -TS -TTY -TUI -UI -URL -US -UX -VCS -VPC -VPN -VS -WARN -WSL -X11 -YAML -``` - -## Code identifiers used in prose (CamelCase, mixedCase) - -```text -apiKey -AppleScript -AssistantMessage -baseURL -BurntSushi -ChatGPT -ClangFormat -CodeCompanion -CodeNomad -DeepSeek -DefaultV2 -FileContent -FileDiff -FileNode -fineGrained -FormatterStatus -GitHub -GitLab -iTerm2 -JavaScript -JetBrains -macOS -mDNS -MiniMax -NeuralNomadsAI -NickvanDyke -NoeFabris -OpenAI -OpenAPI -OpenChamber -OpenCode -OpenRouter -OpenTUI -OpenWork -ownUserPermissions -PowerShell -ProviderAuthAuthorization -ProviderAuthMethod -ProviderInitError -SessionStatus -TabItem -tokenType -ToolIDs -ToolList -TypeScript -typesUrl -UserMessage -VcsInfo -WebView2 -WezTerm -xAI -ZenMux -``` - -## OpenCode CLI commands (as shown in docs) - -```text -opencode -opencode [project] -opencode /path/to/project -opencode acp -opencode agent [command] -opencode agent create -opencode agent list -opencode attach [url] -opencode attach http://10.20.30.40:4096 -opencode attach http://localhost:4096 -opencode auth [command] -opencode auth list -opencode auth login -opencode auth logout -opencode auth ls -opencode export [sessionID] -opencode github [command] -opencode github install -opencode github run -opencode import -opencode import https://opncd.ai/s/abc123 -opencode import session.json -opencode mcp [command] -opencode mcp add -opencode mcp auth [name] -opencode mcp auth list -opencode mcp auth ls -opencode mcp auth my-oauth-server -opencode mcp auth sentry -opencode mcp debug -opencode mcp debug my-oauth-server -opencode mcp list -opencode mcp logout [name] -opencode mcp logout my-oauth-server -opencode mcp ls -opencode models --refresh -opencode models [provider] -opencode models anthropic -opencode run [message..] -opencode run Explain the use of context in Go -opencode serve -opencode serve --cors http://localhost:5173 --cors https://app.example.com -opencode serve --hostname 0.0.0.0 --port 4096 -opencode serve [--port ] [--hostname ] [--cors ] -opencode session [command] -opencode session list -opencode session delete -opencode stats -opencode uninstall -opencode upgrade -opencode upgrade [target] -opencode upgrade v0.1.48 -opencode web -opencode web --cors https://example.com -opencode web --hostname 0.0.0.0 -opencode web --mdns -opencode web --mdns --mdns-domain myproject.local -opencode web --port 4096 -opencode web --port 4096 --hostname 0.0.0.0 -opencode.server.close() -``` - -## Slash commands and routes - -```text -/agent -/auth/:id -/clear -/command -/config -/config/providers -/connect -/continue -/doc -/editor -/event -/experimental/tool?provider=

&model= -/experimental/tool/ids -/export -/file?path= -/file/content?path=

-/file/status -/find?pattern= -/find/file -/find/file?query= -/find/symbol?query= -/formatter -/global/event -/global/health -/help -/init -/instance/dispose -/log -/lsp -/mcp -/mnt/ -/mnt/c/ -/mnt/d/ -/models -/oc -/opencode -/path -/project -/project/current -/provider -/provider/{id}/oauth/authorize -/provider/{id}/oauth/callback -/provider/auth -/q -/quit -/redo -/resume -/session -/session/:id -/session/:id/abort -/session/:id/children -/session/:id/command -/session/:id/diff -/session/:id/fork -/session/:id/init -/session/:id/message -/session/:id/message/:messageID -/session/:id/permissions/:permissionID -/session/:id/prompt_async -/session/:id/revert -/session/:id/share -/session/:id/shell -/session/:id/summarize -/session/:id/todo -/session/:id/unrevert -/session/status -/share -/summarize -/theme -/tui -/tui/append-prompt -/tui/clear-prompt -/tui/control/next -/tui/control/response -/tui/execute-command -/tui/open-help -/tui/open-models -/tui/open-sessions -/tui/open-themes -/tui/show-toast -/tui/submit-prompt -/undo -/Users/username -/Users/username/projects/* -/vcs -``` - -## CLI flags and short options - -```text ---agent ---attach ---command ---continue ---cors ---cwd ---days ---dir ---dry-run ---event ---file ---force ---fork ---format ---help ---hostname ---hostname 0.0.0.0 ---keep-config ---keep-data ---log-level ---max-count ---mdns ---mdns-domain ---method ---model ---models ---port ---print-logs ---project ---prompt ---refresh ---session ---share ---title ---token ---tools ---verbose ---version ---wait - --c --d --f --h --m --n --s --v -``` - -## Environment variables - -```text -AI_API_URL -AI_FLOW_CONTEXT -AI_FLOW_EVENT -AI_FLOW_INPUT -AICORE_DEPLOYMENT_ID -AICORE_RESOURCE_GROUP -AICORE_SERVICE_KEY -ANTHROPIC_API_KEY -AWS_ACCESS_KEY_ID -AWS_BEARER_TOKEN_BEDROCK -AWS_PROFILE -AWS_REGION -AWS_ROLE_ARN -AWS_SECRET_ACCESS_KEY -AWS_WEB_IDENTITY_TOKEN_FILE -AZURE_COGNITIVE_SERVICES_RESOURCE_NAME -AZURE_RESOURCE_NAME -CI_PROJECT_DIR -CI_SERVER_FQDN -CI_WORKLOAD_REF -CLOUDFLARE_ACCOUNT_ID -CLOUDFLARE_API_TOKEN -CLOUDFLARE_GATEWAY_ID -CONTEXT7_API_KEY -GITHUB_TOKEN -GITLAB_AI_GATEWAY_URL -GITLAB_HOST -GITLAB_INSTANCE_URL -GITLAB_OAUTH_CLIENT_ID -GITLAB_TOKEN -GITLAB_TOKEN_OPENCODE -GOOGLE_APPLICATION_CREDENTIALS -GOOGLE_CLOUD_PROJECT -HTTP_PROXY -HTTPS_PROXY -K2_ -MY_API_KEY -MY_ENV_VAR -MY_MCP_CLIENT_ID -MY_MCP_CLIENT_SECRET -NO_PROXY -NODE_ENV -NODE_EXTRA_CA_CERTS -NPM_AUTH_TOKEN -OC_ALLOW_WAYLAND -OPENCODE_API_KEY -OPENCODE_AUTH_JSON -OPENCODE_AUTO_SHARE -OPENCODE_CLIENT -OPENCODE_CONFIG -OPENCODE_CONFIG_CONTENT -OPENCODE_CONFIG_DIR -OPENCODE_DISABLE_AUTOCOMPACT -OPENCODE_DISABLE_AUTOUPDATE -OPENCODE_DISABLE_CLAUDE_CODE -OPENCODE_DISABLE_CLAUDE_CODE_PROMPT -OPENCODE_DISABLE_CLAUDE_CODE_SKILLS -OPENCODE_DISABLE_DEFAULT_PLUGINS -OPENCODE_DISABLE_LSP_DOWNLOAD -OPENCODE_DISABLE_MODELS_FETCH -OPENCODE_DISABLE_PRUNE -OPENCODE_DISABLE_TERMINAL_TITLE -OPENCODE_ENABLE_EXA -OPENCODE_ENABLE_EXPERIMENTAL_MODELS -OPENCODE_EXPERIMENTAL -OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS -OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT -OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER -OPENCODE_EXPERIMENTAL_EXA -OPENCODE_EXPERIMENTAL_FILEWATCHER -OPENCODE_EXPERIMENTAL_ICON_DISCOVERY -OPENCODE_EXPERIMENTAL_LSP_TOOL -OPENCODE_EXPERIMENTAL_LSP_TY -OPENCODE_EXPERIMENTAL_MARKDOWN -OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX -OPENCODE_EXPERIMENTAL_OXFMT -OPENCODE_EXPERIMENTAL_PLAN_MODE -OPENCODE_ENABLE_QUESTION_TOOL -OPENCODE_FAKE_VCS -OPENCODE_GIT_BASH_PATH -OPENCODE_MODEL -OPENCODE_MODELS_URL -OPENCODE_PERMISSION -OPENCODE_PORT -OPENCODE_SERVER_PASSWORD -OPENCODE_SERVER_USERNAME -PROJECT_ROOT -RESOURCE_NAME -RUST_LOG -VARIABLE_NAME -VERTEX_LOCATION -XDG_CONFIG_HOME -``` - -## Package/module identifiers - -```text -../../../config.mjs -@astrojs/starlight/components -@opencode-ai/plugin -@opencode-ai/sdk -path -shescape -zod - -@ -@ai-sdk/anthropic -@ai-sdk/cerebras -@ai-sdk/google -@ai-sdk/openai -@ai-sdk/openai-compatible -@File#L37-42 -@modelcontextprotocol/server-everything -@opencode -``` - -## GitHub owner/repo slugs referenced in docs - -```text -24601/opencode-zellij-namer -angristan/opencode-wakatime -anomalyco/opencode -apps/opencode-agent -athal7/opencode-devcontainers -awesome-opencode/awesome-opencode -backnotprop/plannotator -ben-vargas/ai-sdk-provider-opencode-sdk -btriapitsyn/openchamber -BurntSushi/ripgrep -Cluster444/agentic -code-yeongyu/oh-my-opencode -darrenhinde/opencode-agents -different-ai/opencode-scheduler -different-ai/openwork -features/copilot -folke/tokyonight.nvim -franlol/opencode-md-table-formatter -ggml-org/llama.cpp -ghoulr/opencode-websearch-cited.git -H2Shami/opencode-helicone-session -hosenur/portal -jamesmurdza/daytona -jenslys/opencode-gemini-auth -JRedeker/opencode-morph-fast-apply -JRedeker/opencode-shell-strategy -kdcokenny/ocx -kdcokenny/opencode-background-agents -kdcokenny/opencode-notify -kdcokenny/opencode-workspace -kdcokenny/opencode-worktree -login/device -mohak34/opencode-notifier -morhetz/gruvbox -mtymek/opencode-obsidian -NeuralNomadsAI/CodeNomad -nick-vi/opencode-type-inject -NickvanDyke/opencode.nvim -NoeFabris/opencode-antigravity-auth -nordtheme/nord -numman-ali/opencode-openai-codex-auth -olimorris/codecompanion.nvim -panta82/opencode-notificator -rebelot/kanagawa.nvim -remorses/kimaki -sainnhe/everforest -shekohex/opencode-google-antigravity-auth -shekohex/opencode-pty.git -spoons-and-mirrors/subtask2 -sudo-tee/opencode.nvim -supermemoryai/opencode-supermemory -Tarquinen/opencode-dynamic-context-pruning -Th3Whit3Wolf/one-nvim -upstash/context7 -vtemian/micode -vtemian/octto -yetone/avante.nvim -zenobi-us/opencode-plugin-template -zenobi-us/opencode-skillful -``` - -## Paths, filenames, globs, and URLs - -```text -./.opencode/themes/*.json -.//storage/ -./config/#custom-directory -./global/storage/ -.agents/skills/*/SKILL.md -.agents/skills//SKILL.md -.clang-format -.claude -.claude/skills -.claude/skills/*/SKILL.md -.claude/skills//SKILL.md -.env -.github/workflows/opencode.yml -.gitignore -.gitlab-ci.yml -.ignore -.NET SDK -.npmrc -.ocamlformat -.opencode -.opencode/ -.opencode/agents/ -.opencode/commands/ -.opencode/commands/test.md -.opencode/modes/ -.opencode/plans/*.md -.opencode/plugins/ -.opencode/skills//SKILL.md -.opencode/skills/git-release/SKILL.md -.opencode/tools/ -.well-known/opencode -{ type: "raw" \| "patch", content: string } -{file:path/to/file} -**/*.js -%USERPROFILE%/intelephense/license.txt -%USERPROFILE%\.cache\opencode -%USERPROFILE%\.config\opencode\opencode.jsonc -%USERPROFILE%\.config\opencode\plugins -%USERPROFILE%\.local\share\opencode -%USERPROFILE%\.local\share\opencode\log -/.opencode/themes/*.json -/ -/.opencode/plugins/ -~ -~/... -~/.agents/skills/*/SKILL.md -~/.agents/skills//SKILL.md -~/.aws/credentials -~/.bashrc -~/.cache/opencode -~/.cache/opencode/node_modules/ -~/.claude/CLAUDE.md -~/.claude/skills/ -~/.claude/skills/*/SKILL.md -~/.claude/skills//SKILL.md -~/.config/opencode -~/.config/opencode/AGENTS.md -~/.config/opencode/agents/ -~/.config/opencode/commands/ -~/.config/opencode/modes/ -~/.config/opencode/opencode.json -~/.config/opencode/opencode.jsonc -~/.config/opencode/plugins/ -~/.config/opencode/skills/*/SKILL.md -~/.config/opencode/skills//SKILL.md -~/.config/opencode/themes/*.json -~/.config/opencode/tools/ -~/.config/zed/settings.json -~/.local/share -~/.local/share/opencode/ -~/.local/share/opencode/auth.json -~/.local/share/opencode/log/ -~/.local/share/opencode/mcp-auth.json -~/.local/share/opencode/opencode.jsonc -~/.npmrc -~/.zshrc -~/code/ -~/Library/Application Support -~/projects/* -~/projects/personal/ -${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts -$HOME/intelephense/license.txt -$HOME/projects/* -$XDG_CONFIG_HOME/opencode/themes/*.json -agent/ -agents/ -build/ -commands/ -dist/ -http://:4096 -http://127.0.0.1:8080/callback -http://localhost: -http://localhost:4096 -http://localhost:4096/doc -https://app.example.com -https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/ -https://opencode.ai/zen/v1/chat/completions -https://opencode.ai/zen/v1/messages -https://opencode.ai/zen/v1/models/gemini-3-flash -https://opencode.ai/zen/v1/models/gemini-3-pro -https://opencode.ai/zen/v1/responses -https://RESOURCE_NAME.openai.azure.com/ -laravel/pint -log/ -model: "anthropic/claude-sonnet-4-5" -modes/ -node_modules/ -openai/gpt-4.1 -opencode.ai/config.json -opencode/ -opencode/gpt-5.1-codex -opencode/gpt-5.2-codex -opencode/kimi-k2 -openrouter/google/gemini-2.5-flash -opncd.ai/s/ -packages/*/AGENTS.md -plugins/ -project/ -provider_id/model_id -provider/model -provider/model-id -rm -rf ~/.cache/opencode -skills/ -skills/*/SKILL.md -src/**/*.ts -themes/ -tools/ -``` - -## Keybind strings - -```text -alt+b -Alt+Ctrl+K -alt+d -alt+f -Cmd+Esc -Cmd+Option+K -Cmd+Shift+Esc -Cmd+Shift+G -Cmd+Shift+P -ctrl+a -ctrl+b -ctrl+d -ctrl+e -Ctrl+Esc -ctrl+f -ctrl+g -ctrl+k -Ctrl+Shift+Esc -Ctrl+Shift+P -ctrl+t -ctrl+u -ctrl+w -ctrl+x -DELETE -Shift+Enter -WIN+R -``` - -## Model ID strings referenced - -```text -{env:OPENCODE_MODEL} -anthropic/claude-3-5-sonnet-20241022 -anthropic/claude-haiku-4-20250514 -anthropic/claude-haiku-4-5 -anthropic/claude-sonnet-4-20250514 -anthropic/claude-sonnet-4-5 -gitlab/duo-chat-haiku-4-5 -lmstudio/google/gemma-3n-e4b -openai/gpt-4.1 -openai/gpt-5 -opencode/gpt-5.1-codex -opencode/gpt-5.2-codex -opencode/kimi-k2 -openrouter/google/gemini-2.5-flash -``` diff --git a/.opencode/command/translate.md b/.opencode/command/translate.md new file mode 100644 index 0000000000..ed185b1e28 --- /dev/null +++ b/.opencode/command/translate.md @@ -0,0 +1,14 @@ +--- +description: translate English to other languages +model: opencode/claude-opus-4-7 +--- + +run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time. + +Requirements: + +- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure). +- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks. +- Also preserve every term listed in the Do-Not-Translate glossary below. +- Also apply locale-specific guidance from `.opencode/glossary/.md` when available (for example, `zh-cn.md`). +- Do not modify fenced code blocks. diff --git a/packages/web/src/content/docs/ar/zen.mdx b/packages/web/src/content/docs/ar/zen.mdx index 9f7ed302ed..a2e2aacfe9 100644 --- a/packages/web/src/content/docs/ar/zen.mdx +++ b/packages/web/src/content/docs/ar/zen.mdx @@ -144,7 +144,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -204,15 +203,23 @@ https://opencode.ai/zen/v1/models ### النماذج المهملة -| النموذج | تاريخ الإيقاف | -| ---------------- | ------------- | -| MiniMax M2.1 | 15 مارس 2026 | -| GLM 4.7 | 15 مارس 2026 | -| GLM 4.6 | 15 مارس 2026 | -| Gemini 3 Pro | 9 مارس 2026 | -| Kimi K2 Thinking | 6 مارس 2026 | -| Kimi K2 | 6 مارس 2026 | -| Qwen3 Coder 480B | 6 فبراير 2026 | +| النموذج | تاريخ الإيقاف | +| ------------------ | -------------- | +| GPT 5.2 Codex | 23 يوليو 2026 | +| GPT 5.1 Codex | 23 يوليو 2026 | +| GPT 5.1 Codex Max | 23 يوليو 2026 | +| GPT 5.1 Codex Mini | 23 يوليو 2026 | +| GPT 5 Codex | 23 يوليو 2026 | +| Claude Sonnet 4 | 15 يونيو 2026 | +| GLM 5 | 14 مايو 2026 | +| MiniMax M2.1 | 15 مارس 2026 | +| GLM 4.7 | 15 مارس 2026 | +| GLM 4.6 | 15 مارس 2026 | +| Gemini 3 Pro | 9 مارس 2026 | +| Kimi K2 Thinking | 6 مارس 2026 | +| Kimi K2 | 6 مارس 2026 | +| Claude Haiku 3.5 | 16 فبراير 2026 | +| Qwen3 Coder 480B | 6 فبراير 2026 | --- diff --git a/packages/web/src/content/docs/bs/zen.mdx b/packages/web/src/content/docs/bs/zen.mdx index d10e4f87f6..89527763ca 100644 --- a/packages/web/src/content/docs/bs/zen.mdx +++ b/packages/web/src/content/docs/bs/zen.mdx @@ -151,7 +151,6 @@ Podržavamo pay-as-you-go model. Ispod su cijene **po 1M tokena**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -215,15 +214,23 @@ vam ipak može naplatiti više od $20 ako vam stanje padne ispod $5. ### Zastarjeli modeli -| Model | Datum zastarijevanja | -| ---------------- | -------------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Datum zastarijevanja | +| ------------------ | -------------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/da/zen.mdx b/packages/web/src/content/docs/da/zen.mdx index 44df1602ab..009ad42023 100644 --- a/packages/web/src/content/docs/da/zen.mdx +++ b/packages/web/src/content/docs/da/zen.mdx @@ -151,7 +151,6 @@ Vi understøtter en pay-as-you-go-model. Nedenfor er priserne **pr. 1M tokens**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ at opkræve dig mere end $20, hvis din saldo kommer under $5. ### Udfasede modeller -| Model | Udfasningsdato | -| ---------------- | -------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Udfasningsdato | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/de/zen.mdx b/packages/web/src/content/docs/de/zen.mdx index 61a03e7c24..11550f61c3 100644 --- a/packages/web/src/content/docs/de/zen.mdx +++ b/packages/web/src/content/docs/de/zen.mdx @@ -140,7 +140,6 @@ Wir unterstützen ein Pay-as-you-go-Modell. Unten findest du die Preise **pro 1M | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ Angenommen, du setzt ein monatliches Nutzungslimit von $20, dann wird Zen in ein ### Veraltete Modelle -| Model | Deprecation date | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Deprecation date | +| ------------------ | ---------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/es/zen.mdx b/packages/web/src/content/docs/es/zen.mdx index f0df78118c..f1a08c7ba5 100644 --- a/packages/web/src/content/docs/es/zen.mdx +++ b/packages/web/src/content/docs/es/zen.mdx @@ -151,7 +151,6 @@ Admitimos un modelo de pago por uso. A continuación se muestran los precios **p | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ cobrándote más de $20 si tu saldo baja de $5. ### Modelos obsoletos -| Modelo | Fecha de retirada | -| ---------------- | ----------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Modelo | Fecha de retirada | +| ------------------ | ----------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/fr/zen.mdx b/packages/web/src/content/docs/fr/zen.mdx index 09fa4699a8..7710da2259 100644 --- a/packages/web/src/content/docs/fr/zen.mdx +++ b/packages/web/src/content/docs/fr/zen.mdx @@ -140,7 +140,6 @@ Nous prenons en charge un modèle de paiement à l'utilisation. Vous trouverez c | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ Par exemple, si vous définissez une limite d'utilisation mensuelle à $20, Zen ### Modèles obsolètes -| Modèle | Date de dépréciation | -| ---------------- | -------------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Modèle | Date de dépréciation | +| ------------------ | -------------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/it/zen.mdx b/packages/web/src/content/docs/it/zen.mdx index 36c4fd3e84..a3b8725535 100644 --- a/packages/web/src/content/docs/it/zen.mdx +++ b/packages/web/src/content/docs/it/zen.mdx @@ -151,7 +151,6 @@ Supportiamo un modello pay-as-you-go. Qui sotto trovi i prezzi **per 1M token**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ per addebitarti più di $20 se il tuo saldo scende sotto $5. ### Modelli deprecati -| Modello | Data di deprecazione | -| ---------------- | -------------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Modello | Data di deprecazione | +| ------------------ | -------------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/ja/zen.mdx b/packages/web/src/content/docs/ja/zen.mdx index 504f1fd786..8fcdc6d46b 100644 --- a/packages/web/src/content/docs/ja/zen.mdx +++ b/packages/web/src/content/docs/ja/zen.mdx @@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models ### 非推奨モデル -| Model | Deprecation date | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Deprecation date | +| ------------------ | ---------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/ko/zen.mdx b/packages/web/src/content/docs/ko/zen.mdx index 222f494bcf..eb99c29fe6 100644 --- a/packages/web/src/content/docs/ko/zen.mdx +++ b/packages/web/src/content/docs/ko/zen.mdx @@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models ### 지원 중단 모델 -| 모델 | 지원 중단 날짜 | -| ---------------- | -------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| 모델 | 지원 중단 날짜 | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/nb/zen.mdx b/packages/web/src/content/docs/nb/zen.mdx index c86c282e7d..8ab1762e1f 100644 --- a/packages/web/src/content/docs/nb/zen.mdx +++ b/packages/web/src/content/docs/nb/zen.mdx @@ -151,7 +151,6 @@ Vi støtter en pay-as-you-go-modell. Nedenfor er prisene **per 1M tokens**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ med å belaste deg mer enn $20 hvis saldoen din går under $5. ### Utfasede modeller -| Modell | Utfasingsdato | -| ---------------- | -------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Modell | Utfasingsdato | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/pl/zen.mdx b/packages/web/src/content/docs/pl/zen.mdx index 0788889656..52906036c0 100644 --- a/packages/web/src/content/docs/pl/zen.mdx +++ b/packages/web/src/content/docs/pl/zen.mdx @@ -151,7 +151,6 @@ Obsługujemy model pay-as-you-go. Poniżej znajdują się ceny **za 1M tokenów* | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -213,17 +212,25 @@ ostatecznie obciążyć Cię kwotą wyższą niż $20, jeśli Twoje saldo spadni --- -### Deprecated models +### Wycofane modele -| Model | Deprecation date | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Data wycofania | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/pt-br/zen.mdx b/packages/web/src/content/docs/pt-br/zen.mdx index a72a9a90bf..b35cfdbde5 100644 --- a/packages/web/src/content/docs/pt-br/zen.mdx +++ b/packages/web/src/content/docs/pt-br/zen.mdx @@ -140,7 +140,6 @@ Oferecemos um modelo pay-as-you-go. Abaixo estão os preços **por 1M tokens**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ Por exemplo, digamos que você defina um limite mensal de uso de $20; o Zen não ### Modelos obsoletos -| Modelo | Data de descontinuação | -| ---------------- | ---------------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Modelo | Data de descontinuação | +| ------------------ | ---------------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/ru/zen.mdx b/packages/web/src/content/docs/ru/zen.mdx index 84fedeb107..919026447f 100644 --- a/packages/web/src/content/docs/ru/zen.mdx +++ b/packages/web/src/content/docs/ru/zen.mdx @@ -151,7 +151,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ https://opencode.ai/zen/v1/models ### Устаревшие модели -| Модель | Дата устаревания | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Модель | Дата устаревания | +| ------------------ | ---------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/th/zen.mdx b/packages/web/src/content/docs/th/zen.mdx index efb896601c..b3914b73c2 100644 --- a/packages/web/src/content/docs/th/zen.mdx +++ b/packages/web/src/content/docs/th/zen.mdx @@ -142,7 +142,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,17 +199,25 @@ https://opencode.ai/zen/v1/models --- -### Deprecated models +### โมเดลที่ถูกยกเลิก -| Model | Deprecation date | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Deprecation date | +| ------------------ | ---------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/tr/zen.mdx b/packages/web/src/content/docs/tr/zen.mdx index c505662cbe..3e53ba40d4 100644 --- a/packages/web/src/content/docs/tr/zen.mdx +++ b/packages/web/src/content/docs/tr/zen.mdx @@ -140,7 +140,6 @@ Kullandıkça öde modelini destekliyoruz. Aşağıda **1M token başına** fiya | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ Ayrıca tüm çalışma alanı için ve ekibinizdeki her üye için aylık kulla ### Kullanımdan kaldırılan modeller -| Model | Kullanımdan kaldırılma tarihi | -| ---------------- | ----------------------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Kullanımdan kaldırılma tarihi | +| ------------------ | ----------------------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/zen.mdx b/packages/web/src/content/docs/zen.mdx index 6ebf78336a..58baceb258 100644 --- a/packages/web/src/content/docs/zen.mdx +++ b/packages/web/src/content/docs/zen.mdx @@ -151,7 +151,6 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**. | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -214,15 +213,23 @@ charging you more than $20 if your balance goes below $5. ### Deprecated models -| Model | Deprecation date | -| ---------------- | ---------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| Model | Deprecation date | +| ------------------ | ---------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/zh-cn/zen.mdx b/packages/web/src/content/docs/zh-cn/zen.mdx index 9248ff174c..03124a34f4 100644 --- a/packages/web/src/content/docs/zh-cn/zen.mdx +++ b/packages/web/src/content/docs/zh-cn/zen.mdx @@ -140,7 +140,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -200,15 +199,23 @@ https://opencode.ai/zen/v1/models ### 已弃用模型 -| 模型 | 弃用日期 | -| ---------------- | -------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| 模型 | 弃用日期 | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- diff --git a/packages/web/src/content/docs/zh-tw/zen.mdx b/packages/web/src/content/docs/zh-tw/zen.mdx index 3be6a3da00..ebd48dea8f 100644 --- a/packages/web/src/content/docs/zh-tw/zen.mdx +++ b/packages/web/src/content/docs/zh-tw/zen.mdx @@ -145,7 +145,6 @@ https://opencode.ai/zen/v1/models | Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | | Gemini 3.1 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | | Gemini 3.1 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | | Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | @@ -207,15 +206,23 @@ https://opencode.ai/zen/v1/models ### 已棄用模型 -| 模型 | 棄用日期 | -| ---------------- | -------------- | -| MiniMax M2.1 | March 15, 2026 | -| GLM 4.7 | March 15, 2026 | -| GLM 4.6 | March 15, 2026 | -| Gemini 3 Pro | March 9, 2026 | -| Kimi K2 Thinking | March 6, 2026 | -| Kimi K2 | March 6, 2026 | -| Qwen3 Coder 480B | Feb 6, 2026 | +| 模型 | 棄用日期 | +| ------------------ | -------------- | +| GPT 5.2 Codex | July 23, 2026 | +| GPT 5.1 Codex | July 23, 2026 | +| GPT 5.1 Codex Max | July 23, 2026 | +| GPT 5.1 Codex Mini | July 23, 2026 | +| GPT 5 Codex | July 23, 2026 | +| Claude Sonnet 4 | June 15, 2026 | +| GLM 5 | May 14, 2026 | +| MiniMax M2.1 | March 15, 2026 | +| GLM 4.7 | March 15, 2026 | +| GLM 4.6 | March 15, 2026 | +| Gemini 3 Pro | March 9, 2026 | +| Kimi K2 Thinking | March 6, 2026 | +| Kimi K2 | March 6, 2026 | +| Claude Haiku 3.5 | Feb 16, 2026 | +| Qwen3 Coder 480B | Feb 6, 2026 | --- From 5186c6964b3e7a9a7af31b4e4f0bc29e2fb92d7b Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Apr 2026 13:11:09 -0400 Subject: [PATCH 26/34] sync --- packages/web/src/content/docs/ar/go.mdx | 2 +- packages/web/src/content/docs/bs/go.mdx | 2 +- packages/web/src/content/docs/da/go.mdx | 2 +- packages/web/src/content/docs/de/go.mdx | 2 +- packages/web/src/content/docs/es/go.mdx | 2 +- packages/web/src/content/docs/fr/go.mdx | 2 +- packages/web/src/content/docs/go.mdx | 32 +++++++++++----------- packages/web/src/content/docs/it/go.mdx | 2 +- packages/web/src/content/docs/ja/go.mdx | 2 +- packages/web/src/content/docs/ko/go.mdx | 2 +- packages/web/src/content/docs/nb/go.mdx | 2 +- packages/web/src/content/docs/pl/go.mdx | 2 +- packages/web/src/content/docs/pt-br/go.mdx | 2 +- packages/web/src/content/docs/ru/go.mdx | 2 +- packages/web/src/content/docs/th/go.mdx | 2 +- packages/web/src/content/docs/tr/go.mdx | 2 +- packages/web/src/content/docs/zh-cn/go.mdx | 2 +- packages/web/src/content/docs/zh-tw/go.mdx | 2 +- 18 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/web/src/content/docs/ar/go.mdx b/packages/web/src/content/docs/ar/go.mdx index 246e86edd8..4002e350de 100644 --- a/packages/web/src/content/docs/ar/go.mdx +++ b/packages/web/src/content/docs/ar/go.mdx @@ -93,7 +93,7 @@ OpenCode Go حاليًا في المرحلة التجريبية. | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | diff --git a/packages/web/src/content/docs/bs/go.mdx b/packages/web/src/content/docs/bs/go.mdx index c54350696f..4722553eb2 100644 --- a/packages/web/src/content/docs/bs/go.mdx +++ b/packages/web/src/content/docs/bs/go.mdx @@ -103,7 +103,7 @@ Tabela ispod pruža procijenjeni broj zahtjeva na osnovu tipičnih obrazaca kori | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/da/go.mdx b/packages/web/src/content/docs/da/go.mdx index 20b3ad207c..94e267efdb 100644 --- a/packages/web/src/content/docs/da/go.mdx +++ b/packages/web/src/content/docs/da/go.mdx @@ -103,7 +103,7 @@ Tabellen nedenfor giver et estimeret antal anmodninger baseret på typiske Go-fo | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/de/go.mdx b/packages/web/src/content/docs/de/go.mdx index b79aa96abb..5b6a792d59 100644 --- a/packages/web/src/content/docs/de/go.mdx +++ b/packages/web/src/content/docs/de/go.mdx @@ -95,7 +95,7 @@ Die folgende Tabelle zeigt eine geschätzte Anzahl von Anfragen basierend auf ty | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/es/go.mdx b/packages/web/src/content/docs/es/go.mdx index 31ba3f45ff..87113da9a0 100644 --- a/packages/web/src/content/docs/es/go.mdx +++ b/packages/web/src/content/docs/es/go.mdx @@ -103,7 +103,7 @@ La siguiente tabla proporciona una cantidad estimada de peticiones basada en los | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/fr/go.mdx b/packages/web/src/content/docs/fr/go.mdx index 51897e8a42..5754173eab 100644 --- a/packages/web/src/content/docs/fr/go.mdx +++ b/packages/web/src/content/docs/fr/go.mdx @@ -93,7 +93,7 @@ Le tableau ci-dessous fournit une estimation du nombre de requêtes basée sur d | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/go.mdx b/packages/web/src/content/docs/go.mdx index 8e85fb03cc..fca9337f4c 100644 --- a/packages/web/src/content/docs/go.mdx +++ b/packages/web/src/content/docs/go.mdx @@ -94,22 +94,22 @@ Limits are defined in dollar value. This means your actual request count depends The table below provides an estimated request count based on typical Go usage patterns: -| Model | requests per 5 hour | requests per week | requests per month | -| ----------------- | ------------------- | ----------------- | ------------------ | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| Model | requests per 5 hour | requests per week | requests per month | +| ------------------ | ------------------- | ----------------- | ------------------ | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Estimates are based on observed average request patterns: diff --git a/packages/web/src/content/docs/it/go.mdx b/packages/web/src/content/docs/it/go.mdx index 03c1c10ca6..0bb9a8022e 100644 --- a/packages/web/src/content/docs/it/go.mdx +++ b/packages/web/src/content/docs/it/go.mdx @@ -101,7 +101,7 @@ La tabella seguente fornisce una stima del conteggio delle richieste in base a p | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/ja/go.mdx b/packages/web/src/content/docs/ja/go.mdx index 1a110a700c..11fad87f44 100644 --- a/packages/web/src/content/docs/ja/go.mdx +++ b/packages/web/src/content/docs/ja/go.mdx @@ -93,7 +93,7 @@ OpenCode Goには以下の制限が含まれています: | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/ko/go.mdx b/packages/web/src/content/docs/ko/go.mdx index 445cd86578..61c5466821 100644 --- a/packages/web/src/content/docs/ko/go.mdx +++ b/packages/web/src/content/docs/ko/go.mdx @@ -93,7 +93,7 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다. | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/nb/go.mdx b/packages/web/src/content/docs/nb/go.mdx index ab3676d6d1..3d676c0d62 100644 --- a/packages/web/src/content/docs/nb/go.mdx +++ b/packages/web/src/content/docs/nb/go.mdx @@ -103,7 +103,7 @@ Tabellen nedenfor gir et estimert antall forespørsler basert på typiske bruksm | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/pl/go.mdx b/packages/web/src/content/docs/pl/go.mdx index b984f08590..cec74defc7 100644 --- a/packages/web/src/content/docs/pl/go.mdx +++ b/packages/web/src/content/docs/pl/go.mdx @@ -97,7 +97,7 @@ Poniższa tabela przedstawia szacunkową liczbę żądań na podstawie typowych | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/pt-br/go.mdx b/packages/web/src/content/docs/pt-br/go.mdx index 2094b5a57a..518936bf7e 100644 --- a/packages/web/src/content/docs/pt-br/go.mdx +++ b/packages/web/src/content/docs/pt-br/go.mdx @@ -103,7 +103,7 @@ A tabela abaixo fornece uma contagem estimada de requisições com base nos padr | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/ru/go.mdx b/packages/web/src/content/docs/ru/go.mdx index 0c257153bd..e6e1418050 100644 --- a/packages/web/src/content/docs/ru/go.mdx +++ b/packages/web/src/content/docs/ru/go.mdx @@ -103,7 +103,7 @@ OpenCode Go включает следующие лимиты: | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/th/go.mdx b/packages/web/src/content/docs/th/go.mdx index 153f61368d..d434c371d7 100644 --- a/packages/web/src/content/docs/th/go.mdx +++ b/packages/web/src/content/docs/th/go.mdx @@ -93,7 +93,7 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้: | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/tr/go.mdx b/packages/web/src/content/docs/tr/go.mdx index 77b0eb8c7f..2e62d5ba65 100644 --- a/packages/web/src/content/docs/tr/go.mdx +++ b/packages/web/src/content/docs/tr/go.mdx @@ -93,7 +93,7 @@ Aşağıdaki tablo, tipik Go kullanım modellerine dayalı tahmini bir istek say | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | diff --git a/packages/web/src/content/docs/zh-cn/go.mdx b/packages/web/src/content/docs/zh-cn/go.mdx index 04156059a7..e289cf5236 100644 --- a/packages/web/src/content/docs/zh-cn/go.mdx +++ b/packages/web/src/content/docs/zh-cn/go.mdx @@ -93,7 +93,7 @@ OpenCode Go 包含以下限制: | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | diff --git a/packages/web/src/content/docs/zh-tw/go.mdx b/packages/web/src/content/docs/zh-tw/go.mdx index a466ccba3b..f2f8cd497f 100644 --- a/packages/web/src/content/docs/zh-tw/go.mdx +++ b/packages/web/src/content/docs/zh-tw/go.mdx @@ -93,7 +93,7 @@ OpenCode Go 包含以下限制: | MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | | MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2.5 | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | | Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | | MiniMax M2.7 | 3,400 | 8,500 | 17,000 | | MiniMax M2.5 | 6,300 | 15,900 | 31,800 | From bbb56c2a88970ed37817b297376679087dea78ea Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 17:14:24 +0000 Subject: [PATCH 27/34] chore: generate --- packages/web/src/content/docs/ar/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/bs/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/da/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/de/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/es/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/fr/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/it/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/ja/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/ko/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/nb/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/pl/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/pt-br/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/ru/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/th/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/tr/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/zh-cn/go.mdx | 30 +++++++++++----------- packages/web/src/content/docs/zh-tw/go.mdx | 30 +++++++++++----------- 17 files changed, 255 insertions(+), 255 deletions(-) diff --git a/packages/web/src/content/docs/ar/go.mdx b/packages/web/src/content/docs/ar/go.mdx index 4002e350de..9e5d52c60b 100644 --- a/packages/web/src/content/docs/ar/go.mdx +++ b/packages/web/src/content/docs/ar/go.mdx @@ -84,22 +84,22 @@ OpenCode Go حاليًا في المرحلة التجريبية. يوضح الجدول أدناه عددًا تقديريًا للطلبات بناءً على أنماط استخدام Go المعتادة: -| Model | الطلبات لكل 5 ساعات | الطلبات في الأسبوع | الطلبات في الشهر | -| ----------------- | ------------------- | ------------------ | ---------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | الطلبات لكل 5 ساعات | الطلبات في الأسبوع | الطلبات في الشهر | +| ------------------ | ------------------- | ------------------ | ---------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | تستند التقديرات إلى متوسطات أنماط الطلبات المرصودة: diff --git a/packages/web/src/content/docs/bs/go.mdx b/packages/web/src/content/docs/bs/go.mdx index 4722553eb2..86418c335b 100644 --- a/packages/web/src/content/docs/bs/go.mdx +++ b/packages/web/src/content/docs/bs/go.mdx @@ -94,22 +94,22 @@ Ograničenja su definisana u dolarskoj vrijednosti. To znači da vaš stvarni br Tabela ispod pruža procijenjeni broj zahtjeva na osnovu tipičnih obrazaca korištenja Go pretplate: -| Model | zahtjeva na 5 sati | zahtjeva sedmično | zahtjeva mjesečno | -| ----------------- | ------------------ | ----------------- | ----------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | zahtjeva na 5 sati | zahtjeva sedmično | zahtjeva mjesečno | +| ------------------ | ------------------ | ----------------- | ----------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Procjene se zasnivaju na zapaženim prosječnim obrascima zahtjeva: diff --git a/packages/web/src/content/docs/da/go.mdx b/packages/web/src/content/docs/da/go.mdx index 94e267efdb..70d9e9c8f7 100644 --- a/packages/web/src/content/docs/da/go.mdx +++ b/packages/web/src/content/docs/da/go.mdx @@ -94,22 +94,22 @@ Grænserne er defineret i dollarværdi. Det betyder, at dit faktiske antal anmod Tabellen nedenfor giver et estimeret antal anmodninger baseret på typiske Go-forbrugsmønstre: -| Model | anmodninger pr. 5 timer | anmodninger pr. uge | anmodninger pr. måned | -| ----------------- | ----------------------- | ------------------- | --------------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | anmodninger pr. 5 timer | anmodninger pr. uge | anmodninger pr. måned | +| ------------------ | ----------------------- | ------------------- | --------------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Estimaterne er baseret på observerede gennemsnitlige anmodningsmønstre: diff --git a/packages/web/src/content/docs/de/go.mdx b/packages/web/src/content/docs/de/go.mdx index 5b6a792d59..7bc9f3e279 100644 --- a/packages/web/src/content/docs/de/go.mdx +++ b/packages/web/src/content/docs/de/go.mdx @@ -86,22 +86,22 @@ Limits sind in Dollarwerten definiert. Das bedeutet, dass die tatsächliche Anza Die folgende Tabelle zeigt eine geschätzte Anzahl von Anfragen basierend auf typischen Go-Nutzungsmustern: -| Model | Anfragen pro 5 Stunden | Anfragen pro Woche | Anfragen pro Monat | -| ----------------- | ---------------------- | ------------------ | ------------------ | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | Anfragen pro 5 Stunden | Anfragen pro Woche | Anfragen pro Monat | +| ------------------ | ---------------------- | ------------------ | ------------------ | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Die Schätzungen basieren auf beobachteten durchschnittlichen Anfragemustern: diff --git a/packages/web/src/content/docs/es/go.mdx b/packages/web/src/content/docs/es/go.mdx index 87113da9a0..e346ba2e86 100644 --- a/packages/web/src/content/docs/es/go.mdx +++ b/packages/web/src/content/docs/es/go.mdx @@ -94,22 +94,22 @@ Los límites se definen en valor en dólares. Esto significa que tu cantidad rea La siguiente tabla proporciona una cantidad estimada de peticiones basada en los patrones típicos de uso de Go: -| Model | peticiones por 5 horas | peticiones por semana | peticiones por mes | -| ----------------- | ---------------------- | --------------------- | ------------------ | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | peticiones por 5 horas | peticiones por semana | peticiones por mes | +| ------------------ | ---------------------- | --------------------- | ------------------ | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Las estimaciones se basan en los patrones de peticiones promedio observados: diff --git a/packages/web/src/content/docs/fr/go.mdx b/packages/web/src/content/docs/fr/go.mdx index 5754173eab..219a17ebfe 100644 --- a/packages/web/src/content/docs/fr/go.mdx +++ b/packages/web/src/content/docs/fr/go.mdx @@ -84,22 +84,22 @@ Les limites sont définies en valeur monétaire (dollars). Cela signifie que vot Le tableau ci-dessous fournit une estimation du nombre de requêtes basée sur des modèles d'utilisation typiques de Go : -| Model | requêtes par 5 heures | requêtes par semaine | requêtes par mois | -| ----------------- | --------------------- | -------------------- | ----------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | requêtes par 5 heures | requêtes par semaine | requêtes par mois | +| ------------------ | --------------------- | -------------------- | ----------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Les estimations sont basées sur les modèles de requêtes moyens observés : diff --git a/packages/web/src/content/docs/it/go.mdx b/packages/web/src/content/docs/it/go.mdx index 0bb9a8022e..023ee053aa 100644 --- a/packages/web/src/content/docs/it/go.mdx +++ b/packages/web/src/content/docs/it/go.mdx @@ -92,22 +92,22 @@ I limiti sono definiti in valore in dollari. Questo significa che il conteggio e La tabella seguente fornisce una stima del conteggio delle richieste in base a pattern di utilizzo tipici di Go: -| Model | richieste ogni 5 ore | richieste a settimana | richieste al mese | -| ----------------- | -------------------- | --------------------- | ----------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | richieste ogni 5 ore | richieste a settimana | richieste al mese | +| ------------------ | -------------------- | --------------------- | ----------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Le stime si basano sui pattern medi di richieste osservati: diff --git a/packages/web/src/content/docs/ja/go.mdx b/packages/web/src/content/docs/ja/go.mdx index 11fad87f44..207a47dd93 100644 --- a/packages/web/src/content/docs/ja/go.mdx +++ b/packages/web/src/content/docs/ja/go.mdx @@ -84,22 +84,22 @@ OpenCode Goには以下の制限が含まれています: 以下の表は、一般的なGoの利用パターンに基づいた推定リクエスト数を示しています: -| Model | 5時間あたりのリクエスト数 | 週間リクエスト数 | 月間リクエスト数 | -| ----------------- | ------------------------- | ---------------- | ---------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | 5時間あたりのリクエスト数 | 週間リクエスト数 | 月間リクエスト数 | +| ------------------ | ------------------------- | ---------------- | ---------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | 推定値は、観測された平均的なリクエストパターンに基づいています: diff --git a/packages/web/src/content/docs/ko/go.mdx b/packages/web/src/content/docs/ko/go.mdx index 61c5466821..e9d1172326 100644 --- a/packages/web/src/content/docs/ko/go.mdx +++ b/packages/web/src/content/docs/ko/go.mdx @@ -84,22 +84,22 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다. 아래 표는 일반적인 Go 사용 패턴을 기준으로 한 예상 요청 횟수를 보여줍니다. -| Model | 5시간당 요청 횟수 | 주간 요청 횟수 | 월간 요청 횟수 | -| ----------------- | ----------------- | -------------- | -------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | 5시간당 요청 횟수 | 주간 요청 횟수 | 월간 요청 횟수 | +| ------------------ | ----------------- | -------------- | -------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | 예상치는 관찰된 평균 요청 패턴을 기준으로 합니다. diff --git a/packages/web/src/content/docs/nb/go.mdx b/packages/web/src/content/docs/nb/go.mdx index 3d676c0d62..9191b94a31 100644 --- a/packages/web/src/content/docs/nb/go.mdx +++ b/packages/web/src/content/docs/nb/go.mdx @@ -94,22 +94,22 @@ Grensene er definert i dollarverdi. Dette betyr at ditt faktiske antall forespø Tabellen nedenfor gir et estimert antall forespørsler basert på typiske bruksmønstre for Go: -| Model | forespørsler per 5 timer | forespørsler per uke | forespørsler per måned | -| ----------------- | ------------------------ | -------------------- | ---------------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | forespørsler per 5 timer | forespørsler per uke | forespørsler per måned | +| ------------------ | ------------------------ | -------------------- | ---------------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Estimatene er basert på observerte gjennomsnittlige forespørselsmønstre: diff --git a/packages/web/src/content/docs/pl/go.mdx b/packages/web/src/content/docs/pl/go.mdx index cec74defc7..00771c0244 100644 --- a/packages/web/src/content/docs/pl/go.mdx +++ b/packages/web/src/content/docs/pl/go.mdx @@ -88,22 +88,22 @@ Limity są zdefiniowane w wartości w dolarach. Oznacza to, że rzeczywista licz Poniższa tabela przedstawia szacunkową liczbę żądań na podstawie typowych wzorców korzystania z Go: -| Model | żądania na 5 godzin | żądania na tydzień | żądania na miesiąc | -| ----------------- | ------------------- | ------------------ | ------------------ | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | żądania na 5 godzin | żądania na tydzień | żądania na miesiąc | +| ------------------ | ------------------- | ------------------ | ------------------ | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Szacunki opierają się na zaobserwowanych średnich wzorcach żądań: diff --git a/packages/web/src/content/docs/pt-br/go.mdx b/packages/web/src/content/docs/pt-br/go.mdx index 518936bf7e..9fbdd69f3c 100644 --- a/packages/web/src/content/docs/pt-br/go.mdx +++ b/packages/web/src/content/docs/pt-br/go.mdx @@ -94,22 +94,22 @@ Os limites são definidos em valor em dólares. Isso significa que a sua contage A tabela abaixo fornece uma contagem estimada de requisições com base nos padrões típicos de uso do Go: -| Model | requisições por 5 horas | requisições por semana | requisições por mês | -| ----------------- | ----------------------- | ---------------------- | ------------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | requisições por 5 horas | requisições por semana | requisições por mês | +| ------------------ | ----------------------- | ---------------------- | ------------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | As estimativas baseiam-se nos padrões médios de requisições observados: diff --git a/packages/web/src/content/docs/ru/go.mdx b/packages/web/src/content/docs/ru/go.mdx index e6e1418050..e91bc331d3 100644 --- a/packages/web/src/content/docs/ru/go.mdx +++ b/packages/web/src/content/docs/ru/go.mdx @@ -94,22 +94,22 @@ OpenCode Go включает следующие лимиты: В таблице ниже приведено примерное количество запросов на основе типичных сценариев использования Go: -| Model | запросов за 5 часов | запросов в неделю | запросов в месяц | -| ----------------- | ------------------- | ----------------- | ---------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | запросов за 5 часов | запросов в неделю | запросов в месяц | +| ------------------ | ------------------- | ----------------- | ---------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Оценки основаны на наблюдаемых средних показателях запросов: diff --git a/packages/web/src/content/docs/th/go.mdx b/packages/web/src/content/docs/th/go.mdx index d434c371d7..0fb7020f3a 100644 --- a/packages/web/src/content/docs/th/go.mdx +++ b/packages/web/src/content/docs/th/go.mdx @@ -84,22 +84,22 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้: ตารางด้านล่างแสดงจำนวน request โดยประมาณตามรูปแบบการใช้งานปกติของ Go: -| Model | requests ต่อ 5 ชั่วโมง | requests ต่อสัปดาห์ | requests ต่อเดือน | -| ----------------- | ---------------------- | ------------------- | ----------------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | requests ต่อ 5 ชั่วโมง | requests ต่อสัปดาห์ | requests ต่อเดือน | +| ------------------ | ---------------------- | ------------------- | ----------------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | การประมาณการอ้างอิงจากรูปแบบการใช้งาน request โดยเฉลี่ยที่สังเกตพบ: diff --git a/packages/web/src/content/docs/tr/go.mdx b/packages/web/src/content/docs/tr/go.mdx index 2e62d5ba65..37c5268b41 100644 --- a/packages/web/src/content/docs/tr/go.mdx +++ b/packages/web/src/content/docs/tr/go.mdx @@ -84,22 +84,22 @@ Limitler dolar değeri üzerinden belirlenmiştir. Bu, gerçek istek sayınızı Aşağıdaki tablo, tipik Go kullanım modellerine dayalı tahmini bir istek sayısı sunmaktadır: -| Model | 5 saatte bir istek | haftalık istek | aylık istek | -| ----------------- | ------------------ | -------------- | ----------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | 5 saatte bir istek | haftalık istek | aylık istek | +| ------------------ | ------------------ | -------------- | ----------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | Tahminler, gözlemlenen ortalama istek modellerine dayanmaktadır: diff --git a/packages/web/src/content/docs/zh-cn/go.mdx b/packages/web/src/content/docs/zh-cn/go.mdx index e289cf5236..c5d4a381a1 100644 --- a/packages/web/src/content/docs/zh-cn/go.mdx +++ b/packages/web/src/content/docs/zh-cn/go.mdx @@ -84,22 +84,22 @@ OpenCode Go 包含以下限制: 下表提供了基于典型 Go 使用模式的预估请求数: -| Model | 每 5 小时请求数 | 每周请求数 | 每月请求数 | -| ----------------- | --------------- | ---------- | ---------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | 每 5 小时请求数 | 每周请求数 | 每月请求数 | +| ------------------ | --------------- | ---------- | ---------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | 预估值基于观察到的平均请求模式: diff --git a/packages/web/src/content/docs/zh-tw/go.mdx b/packages/web/src/content/docs/zh-tw/go.mdx index f2f8cd497f..290bdac367 100644 --- a/packages/web/src/content/docs/zh-tw/go.mdx +++ b/packages/web/src/content/docs/zh-tw/go.mdx @@ -84,22 +84,22 @@ OpenCode Go 包含以下限制: 下表提供了基於典型 Go 使用模式的預估請求次數: -| Model | 每 5 小時請求數 | 每週請求數 | 每月請求數 | -| ----------------- | --------------- | ---------- | ---------- | -| GLM-5.1 | 880 | 2,150 | 4,300 | -| GLM-5 | 1,150 | 2,880 | 5,750 | -| Kimi K2.5 | 1,850 | 4,630 | 9,250 | -| Kimi K2.6 | 1,150 | 2,880 | 5,750 | -| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | -| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | -| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | +| Model | 每 5 小時請求數 | 每週請求數 | 每月請求數 | +| ------------------ | --------------- | ---------- | ---------- | +| GLM-5.1 | 880 | 2,150 | 4,300 | +| GLM-5 | 1,150 | 2,880 | 5,750 | +| Kimi K2.5 | 1,850 | 4,630 | 9,250 | +| Kimi K2.6 | 1,150 | 2,880 | 5,750 | +| MiMo-V2-Pro | 1,290 | 3,225 | 6,450 | +| MiMo-V2-Omni | 2,150 | 5,450 | 10,900 | +| MiMo-V2.5-Pro | 1,290 | 3,225 | 6,450 | | MiMo-V2.5 (≤ 256K) | 2,150 | 5,450 | 10,900 | -| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | -| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | -| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | -| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | -| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | -| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | +| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 | +| MiniMax M2.7 | 3,400 | 8,500 | 17,000 | +| MiniMax M2.5 | 6,300 | 15,900 | 31,800 | +| Qwen3.5 Plus | 10,200 | 25,200 | 50,500 | +| DeepSeek V4 Pro | 1,300 | 3,250 | 6,500 | +| DeepSeek V4 Flash | 7,450 | 18,600 | 37,300 | 預估值是基於觀察到的平均請求模式: From 00d1a7e090e3b7cc7d3a39e874a02865439d8472 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Sun, 26 Apr 2026 12:32:41 -0500 Subject: [PATCH 28/34] chore: rm empty file --- session.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 session.json diff --git a/session.json b/session.json deleted file mode 100644 index e69de29bb2..0000000000 From dcee1c36426495716b2bacc7f31ccf54d29a3ac0 Mon Sep 17 00:00:00 2001 From: Jermiah Joseph <44614774+jjjermiah@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:21:29 -0400 Subject: [PATCH 29/34] fix(editor): reject lock files with no workspace match for cwd (#24323) --- .../src/cli/cmd/tui/context/editor.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/editor.ts b/packages/opencode/src/cli/cmd/tui/context/editor.ts index 75c5440f5d..72b0785d61 100644 --- a/packages/opencode/src/cli/cmd/tui/context/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/context/editor.ts @@ -278,12 +278,16 @@ function resolveEditorLockFile() { } const cwd = process.cwd() + // longest workspace folder that contains cwd; 0 if none match + const bestMatchLength = (lock: EditorLockFile) => + Math.max(0, ...lock.workspaceFolders.map((folder) => pathContainsLength(folder, cwd))) const locks = entries .filter((entry) => entry.endsWith(".lock")) .map((entry) => readEditorLockFile(path.join(directory, entry))) .filter((entry): entry is EditorLockFile => Boolean(entry)) - .sort((left, right) => scoreEditorLock(right, cwd) - scoreEditorLock(left, cwd)) - + .filter((entry) => bestMatchLength(entry) > 0) + // prefer locks with longer matching workspace folders, then more recent ones + .sort((left, right) => bestMatchLength(right) - bestMatchLength(left) || right.mtimeMs - left.mtimeMs) return locks[0] } @@ -310,11 +314,6 @@ function readEditorLockFile(filePath: string): EditorLockFile | undefined { } } -function scoreEditorLock(lock: EditorLockFile, cwd: string) { - const workspaceMatch = lock.workspaceFolders.some((folder) => pathContains(folder, cwd)) ? 1 : 0 - return workspaceMatch * 1_000_000_000_000 + lock.mtimeMs -} - function editorSelectionKey(selection: EditorSelection | undefined) { if (!selection) return "" return [ @@ -327,9 +326,10 @@ function editorSelectionKey(selection: EditorSelection | undefined) { ].join("\0") } -function pathContains(parent: string, child: string) { - const relative = path.relative(path.resolve(parent), path.resolve(child)) - return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)) +function pathContainsLength(parent: string, child: string) { + const resolved = path.resolve(parent) + const relative = path.relative(resolved, path.resolve(child)) + return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)) ? resolved.length : 0 } function openEditorSocket(connection: EditorConnection) { From 3beadeebff13fefb6f771ce22c3ef7b2736e8245 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 27 Apr 2026 02:46:49 +0800 Subject: [PATCH 30/34] feat(go): add Go model listing endpoint (#24304) Co-authored-by: Frank --- .../app/src/routes/zen/go/v1/models.ts | 10 +++++ .../app/src/routes/zen/util/modelsHandler.ts | 36 +++++++++++++++ .../console/app/src/routes/zen/v1/models.ts | 44 +++---------------- packages/web/src/content/docs/ar/go.mdx | 10 +++++ packages/web/src/content/docs/bs/go.mdx | 10 +++++ packages/web/src/content/docs/da/go.mdx | 10 +++++ packages/web/src/content/docs/de/go.mdx | 10 +++++ packages/web/src/content/docs/es/go.mdx | 10 +++++ packages/web/src/content/docs/fr/go.mdx | 10 +++++ packages/web/src/content/docs/go.mdx | 10 +++++ packages/web/src/content/docs/it/go.mdx | 10 +++++ packages/web/src/content/docs/ja/go.mdx | 10 +++++ packages/web/src/content/docs/ko/go.mdx | 10 +++++ packages/web/src/content/docs/nb/go.mdx | 10 +++++ packages/web/src/content/docs/pl/go.mdx | 10 +++++ packages/web/src/content/docs/pt-br/go.mdx | 10 +++++ packages/web/src/content/docs/ru/go.mdx | 10 +++++ packages/web/src/content/docs/th/go.mdx | 10 +++++ packages/web/src/content/docs/tr/go.mdx | 10 +++++ packages/web/src/content/docs/zh-cn/go.mdx | 10 +++++ packages/web/src/content/docs/zh-tw/go.mdx | 10 +++++ 21 files changed, 233 insertions(+), 37 deletions(-) create mode 100644 packages/console/app/src/routes/zen/go/v1/models.ts create mode 100644 packages/console/app/src/routes/zen/util/modelsHandler.ts diff --git a/packages/console/app/src/routes/zen/go/v1/models.ts b/packages/console/app/src/routes/zen/go/v1/models.ts new file mode 100644 index 0000000000..85db306880 --- /dev/null +++ b/packages/console/app/src/routes/zen/go/v1/models.ts @@ -0,0 +1,10 @@ +import type { APIEvent } from "@solidjs/start/server" +import { getHandler, optionsHandler } from "../../util/modelsHandler" + +export async function OPTIONS(_input: APIEvent) { + return optionsHandler() +} + +export async function GET(input: APIEvent) { + return getHandler({ modelList: "lite" }) +} diff --git a/packages/console/app/src/routes/zen/util/modelsHandler.ts b/packages/console/app/src/routes/zen/util/modelsHandler.ts new file mode 100644 index 0000000000..d924e25c80 --- /dev/null +++ b/packages/console/app/src/routes/zen/util/modelsHandler.ts @@ -0,0 +1,36 @@ +import { ZenData } from "@opencode-ai/console-core/model.js" + +export async function optionsHandler() { + return new Response(null, { + status: 200, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, Authorization", + }, + }) +} + +export async function getHandler(opts: { modelList: "lite" | "full"; disabledModels?: string[] }) { + const zenData = ZenData.list(opts.modelList) + + return new Response( + JSON.stringify({ + object: "list", + data: Object.entries(zenData.models) + .filter(([id]) => !opts.disabledModels?.includes(id)) + .filter(([id]) => !id.startsWith("alpha-")) + .map(([id, _model]) => ({ + id, + object: "model", + created: Math.floor(Date.now() / 1000), + owned_by: "opencode", + })), + }), + { + headers: { + "Content-Type": "application/json", + }, + }, + ) +} diff --git a/packages/console/app/src/routes/zen/v1/models.ts b/packages/console/app/src/routes/zen/v1/models.ts index 6b4a878fc7..d2fc36e459 100644 --- a/packages/console/app/src/routes/zen/v1/models.ts +++ b/packages/console/app/src/routes/zen/v1/models.ts @@ -3,59 +3,29 @@ import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/ind import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js" import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js" -import { ZenData } from "@opencode-ai/console-core/model.js" +import { optionsHandler, getHandler } from "~/routes/zen/util/modelsHandler" export async function OPTIONS(_input: APIEvent) { - return new Response(null, { - status: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, POST, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - }, - }) + return optionsHandler() } export async function GET(input: APIEvent) { - const zenData = ZenData.list("full") - const disabledModels = await authenticate() - - return new Response( - JSON.stringify({ - object: "list", - data: Object.entries(zenData.models) - .filter(([id]) => !disabledModels.includes(id)) - .filter(([id]) => !id.startsWith("alpha-")) - .map(([id, _model]) => ({ - id, - object: "model", - created: Math.floor(Date.now() / 1000), - owned_by: "opencode", - })), - }), - { - headers: { - "Content-Type": "application/json", - }, - }, - ) - - async function authenticate() { + const disabledModels = await (() => { const apiKey = input.request.headers.get("authorization")?.split(" ")[1] if (!apiKey) return [] - const disabledModels = await Database.use((tx) => + return Database.use((tx) => tx .select({ model: ModelTable.model, }) .from(KeyTable) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) - .leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted))) + .innerJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted))) .where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted))) .then((rows) => rows.map((row) => row.model)), ) + })() - return disabledModels - } + return getHandler({ modelList: "full", disabledModels }) } diff --git a/packages/web/src/content/docs/ar/go.mdx b/packages/web/src/content/docs/ar/go.mdx index 9e5d52c60b..f760be36bf 100644 --- a/packages/web/src/content/docs/ar/go.mdx +++ b/packages/web/src/content/docs/ar/go.mdx @@ -155,6 +155,16 @@ OpenCode Go حاليًا في المرحلة التجريبية. --- +### النماذج + +يمكنك جلب القائمة الكاملة بالنماذج المتاحة وبياناتها الوصفية من: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## الخصوصية صُمّمت هذه الخطة أساسًا للمستخدمين الدوليين، مع استضافة النماذج في الولايات المتحدة والاتحاد الأوروبي وسنغافورة لضمان وصول عالمي مستقر. ويتّبع مزودونا سياسة عدم الاحتفاظ بالبيانات، ولا يستخدمون بياناتك لتدريب النماذج. diff --git a/packages/web/src/content/docs/bs/go.mdx b/packages/web/src/content/docs/bs/go.mdx index 86418c335b..7977725ac7 100644 --- a/packages/web/src/content/docs/bs/go.mdx +++ b/packages/web/src/content/docs/bs/go.mdx @@ -169,6 +169,16 @@ koristi format `opencode-go/`. Na primjer, za Kimi K2.6, koristili bis --- +### Modeli + +Pun spisak dostupnih modela i njihovih metapodataka možete preuzeti na: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privatnost Plan je prvenstveno namijenjen međunarodnim korisnicima, a modeli su smješteni u US, EU i Singaporeu radi stabilnog globalnog pristupa. Naši pružaoci usluga primjenjuju politiku nultog zadržavanja podataka i ne koriste vaše podatke za treniranje modela. diff --git a/packages/web/src/content/docs/da/go.mdx b/packages/web/src/content/docs/da/go.mdx index 70d9e9c8f7..123e23c41d 100644 --- a/packages/web/src/content/docs/da/go.mdx +++ b/packages/web/src/content/docs/da/go.mdx @@ -169,6 +169,16 @@ bruge `opencode-go/kimi-k2.6` i din config. --- +### Modeller + +Du kan hente den fulde liste over tilgængelige modeller og deres metadata fra: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privatliv Planen er primært designet til internationale brugere med modeller hostet i US, EU og Singapore for stabil global adgang. Vores udbydere følger en zero-retention-policy og bruger ikke dine data til modeltræning. diff --git a/packages/web/src/content/docs/de/go.mdx b/packages/web/src/content/docs/de/go.mdx index 7bc9f3e279..fc49b9a71d 100644 --- a/packages/web/src/content/docs/de/go.mdx +++ b/packages/web/src/content/docs/de/go.mdx @@ -157,6 +157,16 @@ Die [Modell-ID](/docs/config/#models) in deiner OpenCode Config verwendet das Fo --- +### Models + +Du kannst die vollständige Liste der verfügbaren Modelle und ihrer Metadaten hier abrufen: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Datenschutz Der Plan ist in erster Linie für internationale Nutzer konzipiert, mit in US, EU und Singapore gehosteten Modellen für einen stabilen weltweiten Zugriff. Unsere Anbieter befolgen eine Zero-Retention-Richtlinie und verwenden Ihre Daten nicht für das Modelltraining. diff --git a/packages/web/src/content/docs/es/go.mdx b/packages/web/src/content/docs/es/go.mdx index e346ba2e86..55068577b6 100644 --- a/packages/web/src/content/docs/es/go.mdx +++ b/packages/web/src/content/docs/es/go.mdx @@ -169,6 +169,16 @@ usa el formato `opencode-go/`. Por ejemplo, para Kimi K2.6, usarías --- +### Modelos + +Puedes obtener la lista completa de modelos disponibles y sus metadatos desde: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privacidad El plan está diseñado principalmente para usuarios internacionales, con modelos alojados en US, EU y Singapore para ofrecer un acceso global estable. Nuestros proveedores siguen una política de retención cero y no utilizan tus datos para el entrenamiento de modelos. diff --git a/packages/web/src/content/docs/fr/go.mdx b/packages/web/src/content/docs/fr/go.mdx index 219a17ebfe..8ab2d04df6 100644 --- a/packages/web/src/content/docs/fr/go.mdx +++ b/packages/web/src/content/docs/fr/go.mdx @@ -155,6 +155,16 @@ L'[ID de modèle](/docs/config/#models) dans votre configuration OpenCode utilis --- +### Modèles + +Vous pouvez récupérer la liste complète des modèles disponibles et leurs métadonnées à partir de : + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Confidentialité Cette offre est conçue avant tout pour les utilisateurs internationaux, avec des modèles hébergés aux US, dans l’EU et à Singapore afin d’assurer un accès mondial stable. Nos fournisseurs appliquent une politique de rétention zéro et n’utilisent pas vos données pour l’entraînement des modèles. diff --git a/packages/web/src/content/docs/go.mdx b/packages/web/src/content/docs/go.mdx index fca9337f4c..b12d29c48c 100644 --- a/packages/web/src/content/docs/go.mdx +++ b/packages/web/src/content/docs/go.mdx @@ -169,6 +169,16 @@ use `opencode-go/kimi-k2.6` in your config. --- +### Models + +You can fetch the full list of available models and their metadata from: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privacy The plan is designed primarily for international users, with models hosted in the US, EU, and Singapore for stable global access. Our providers follow a zero-retention policy and do not use your data for model training. diff --git a/packages/web/src/content/docs/it/go.mdx b/packages/web/src/content/docs/it/go.mdx index 023ee053aa..7c31325b14 100644 --- a/packages/web/src/content/docs/it/go.mdx +++ b/packages/web/src/content/docs/it/go.mdx @@ -167,6 +167,16 @@ utilizza il formato `opencode-go/`. Ad esempio, per Kimi K2.6, userest --- +### Modelli + +Puoi recuperare l'elenco completo dei modelli disponibili e i relativi metadati da: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privacy Il piano è pensato principalmente per gli utenti internazionali, con modelli ospitati negli US, nell’EU e a Singapore per un accesso globale stabile. I nostri provider seguono una politica di zero-retention e non utilizzano i tuoi dati per l’addestramento dei modelli. diff --git a/packages/web/src/content/docs/ja/go.mdx b/packages/web/src/content/docs/ja/go.mdx index 207a47dd93..ddfbcd93ec 100644 --- a/packages/web/src/content/docs/ja/go.mdx +++ b/packages/web/src/content/docs/ja/go.mdx @@ -155,6 +155,16 @@ OpenCode設定の[model id](/docs/config/#models)は、`opencode-go/` --- +### モデル + +利用可能なモデルとそのメタデータの完全な一覧は、次から取得できます。 + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## プライバシー このプランは主に海外ユーザー向けに設計されており、安定したグローバルアクセスのため、モデルは US、EU、Singapore でホストされています。各プロバイダーはデータを保持しないポリシーに従っており、お客様のデータをモデルのトレーニングに使用することはありません。 diff --git a/packages/web/src/content/docs/ko/go.mdx b/packages/web/src/content/docs/ko/go.mdx index e9d1172326..23463b61a7 100644 --- a/packages/web/src/content/docs/ko/go.mdx +++ b/packages/web/src/content/docs/ko/go.mdx @@ -155,6 +155,16 @@ OpenCode config의 [model id](/docs/config/#models)는 `opencode-go/` --- +### 모델 + +사용 가능한 전체 모델 목록과 메타데이터는 다음에서 가져올 수 있습니다. + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## 개인정보 보호 이 플랜은 안정적인 전 세계 액세스를 위해 모델을 미국, EU, 싱가포르에 호스팅하며, 주로 해외 사용자를 위해 설계되었습니다. 저희 제공자는 zero-retention 정책을 따르며, 고객 데이터를 모델 학습에 사용하지 않습니다. diff --git a/packages/web/src/content/docs/nb/go.mdx b/packages/web/src/content/docs/nb/go.mdx index 9191b94a31..9caf399904 100644 --- a/packages/web/src/content/docs/nb/go.mdx +++ b/packages/web/src/content/docs/nb/go.mdx @@ -169,6 +169,16 @@ bruke `opencode-go/kimi-k2.6` i konfigurasjonen din. --- +### Modeller + +Du kan hente hele listen over tilgjengelige modeller og metadataene deres fra: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Personvern Planen er primært utformet for internasjonale brukere, med modeller hostet i US, EU og Singapore for stabil global tilgang. Våre leverandører følger en zero-retention-policy og bruker ikke dataene dine til modelltrening. diff --git a/packages/web/src/content/docs/pl/go.mdx b/packages/web/src/content/docs/pl/go.mdx index 00771c0244..e9cf4f7a5d 100644 --- a/packages/web/src/content/docs/pl/go.mdx +++ b/packages/web/src/content/docs/pl/go.mdx @@ -161,6 +161,16 @@ używa formatu `opencode-go/`. Na przykład dla Kimi K2.6 należy uży --- +### Modele + +Pełną listę dostępnych modeli i ich metadane możesz pobrać z: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Prywatność Plan został zaprojektowany przede wszystkim z myślą o użytkownikach międzynarodowych, a modele są hostowane w US, EU i Singapore, aby zapewnić stabilny dostęp na całym świecie. Nasi dostawcy stosują politykę zerowej retencji i nie wykorzystują Twoich danych do trenowania modeli. diff --git a/packages/web/src/content/docs/pt-br/go.mdx b/packages/web/src/content/docs/pt-br/go.mdx index 9fbdd69f3c..7e3d3f9a21 100644 --- a/packages/web/src/content/docs/pt-br/go.mdx +++ b/packages/web/src/content/docs/pt-br/go.mdx @@ -169,6 +169,16 @@ usa o formato `opencode-go/`. Por exemplo, para o Kimi K2.6, você usa --- +### Modelos + +Você pode buscar a lista completa de modelos disponíveis e seus metadados em: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privacidade O plano foi projetado principalmente para usuários internacionais, com modelos hospedados em US, EU e Singapore para garantir acesso global estável. Nossos provedores seguem uma política de retenção zero e não usam seus dados para treinamento de modelos. diff --git a/packages/web/src/content/docs/ru/go.mdx b/packages/web/src/content/docs/ru/go.mdx index e91bc331d3..9ff1ae1410 100644 --- a/packages/web/src/content/docs/ru/go.mdx +++ b/packages/web/src/content/docs/ru/go.mdx @@ -169,6 +169,16 @@ OpenCode Go включает следующие лимиты: --- +### Модели + +Вы можете получить полный список доступных моделей и их метаданных по адресу: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Конфиденциальность Этот план разработан в первую очередь для международных пользователей: модели размещены в US, EU и Singapore, чтобы обеспечить стабильный доступ по всему миру. Наши провайдеры придерживаются политики zero-retention и не используют ваши данные для обучения моделей. diff --git a/packages/web/src/content/docs/th/go.mdx b/packages/web/src/content/docs/th/go.mdx index 0fb7020f3a..9a5d4c634f 100644 --- a/packages/web/src/content/docs/th/go.mdx +++ b/packages/web/src/content/docs/th/go.mdx @@ -155,6 +155,16 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้: --- +### Models + +คุณสามารถดึงรายการโมเดลทั้งหมดที่พร้อมใช้งานและ metadata ของมันได้จาก: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Privacy แพลนนี้ออกแบบมาสำหรับผู้ใช้ทั่วโลกเป็นหลัก โดยโฮสต์โมเดลไว้ใน US, EU และ Singapore เพื่อให้เข้าถึงได้อย่างเสถียรจากทั่วโลก ผู้ให้บริการของเราปฏิบัติตามนโยบาย zero-retention และไม่นำข้อมูลของคุณไปใช้ในการฝึกโมเดล diff --git a/packages/web/src/content/docs/tr/go.mdx b/packages/web/src/content/docs/tr/go.mdx index 37c5268b41..ab488eb257 100644 --- a/packages/web/src/content/docs/tr/go.mdx +++ b/packages/web/src/content/docs/tr/go.mdx @@ -155,6 +155,16 @@ OpenCode yapılandırmanızdaki [model id](/docs/config/#models) formatı `openc --- +### Modeller + +Mevcut modellerin tam listesini ve metadata'larını şuradan alabilirsiniz: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## Gizlilik Plan, öncelikle uluslararası kullanıcılar için tasarlanmıştır; dünya genelinde istikrarlı erişim sağlamak için modeller US, EU ve Singapore'da barındırılır. Sağlayıcılarımız sıfır veri saklama politikası uygular ve verilerinizi model eğitimi için kullanmaz. diff --git a/packages/web/src/content/docs/zh-cn/go.mdx b/packages/web/src/content/docs/zh-cn/go.mdx index c5d4a381a1..10ec977785 100644 --- a/packages/web/src/content/docs/zh-cn/go.mdx +++ b/packages/web/src/content/docs/zh-cn/go.mdx @@ -155,6 +155,16 @@ OpenCode Go 包含以下限制: --- +### 模型 + +你可以从以下地址获取可用模型及其元数据的完整列表: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## 隐私保护 该方案主要面向国际用户,模型托管在 US、EU 和 Singapore,以提供稳定的全球访问。我们的提供商遵循零保留政策,不会将您的数据用于模型训练。 diff --git a/packages/web/src/content/docs/zh-tw/go.mdx b/packages/web/src/content/docs/zh-tw/go.mdx index 290bdac367..9e460b9678 100644 --- a/packages/web/src/content/docs/zh-tw/go.mdx +++ b/packages/web/src/content/docs/zh-tw/go.mdx @@ -155,6 +155,16 @@ OpenCode Go 包含以下限制: --- +### 模型 + +你可以從以下位置取得所有可用模型及其中繼資料的完整清單: + +``` +https://opencode.ai/zen/go/v1/models +``` + +--- + ## 隱私權 此方案主要為國際使用者設計,模型部署於 US、EU 與 Singapore,以提供穩定的全球存取體驗。我們的供應商遵循零保留政策,不會將你的資料用於模型訓練。 From 21e01dbe04acc49fb758abb91bf3ec80dd8a90b1 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 26 Apr 2026 22:31:33 +0200 Subject: [PATCH 31/34] upgrade opentui to 0.1.104 (#24531) --- bun.lock | 24 ++--- package.json | 4 +- .../opencode/src/cli/cmd/tui/util/index.ts | 1 - .../opencode/src/cli/cmd/tui/util/terminal.ts | 96 ------------------- packages/plugin/package.json | 4 +- 5 files changed, 16 insertions(+), 113 deletions(-) delete mode 100644 packages/opencode/src/cli/cmd/tui/util/terminal.ts diff --git a/bun.lock b/bun.lock index 6a146d9e84..8a3684c0b0 100644 --- a/bun.lock +++ b/bun.lock @@ -506,8 +506,8 @@ "typescript": "catalog:", }, "peerDependencies": { - "@opentui/core": ">=0.1.103", - "@opentui/solid": ">=0.1.103", + "@opentui/core": ">=0.1.104", + "@opentui/solid": ">=0.1.104", }, "optionalPeers": [ "@opentui/core", @@ -685,8 +685,8 @@ "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@opentui/core": "0.1.103", - "@opentui/solid": "0.1.103", + "@opentui/core": "0.1.104", + "@opentui/solid": "0.1.104", "@pierre/diffs": "1.1.0-beta.18", "@playwright/test": "1.59.1", "@solid-primitives/storage": "4.3.3", @@ -1613,21 +1613,21 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], - "@opentui/core": ["@opentui/core@0.1.103", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.103", "@opentui/core-darwin-x64": "0.1.103", "@opentui/core-linux-arm64": "0.1.103", "@opentui/core-linux-x64": "0.1.103", "@opentui/core-win32-arm64": "0.1.103", "@opentui/core-win32-x64": "0.1.103", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-PWVv/bDmlk1i6X1f0zXs+jSaTrQ/ByX8wFbP2WinOObTGf//UbcRP4dbWxPXvOyka9QlmRBG/7GbloQSIStyVw=="], + "@opentui/core": ["@opentui/core@0.1.104", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.104", "@opentui/core-darwin-x64": "0.1.104", "@opentui/core-linux-arm64": "0.1.104", "@opentui/core-linux-x64": "0.1.104", "@opentui/core-win32-arm64": "0.1.104", "@opentui/core-win32-x64": "0.1.104", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-29RZ+f7sw5B6ebDFRuhrDs2UOV7vR76npr0vVRAJAmIMtdLb4Dse35Y0Hk6WerFKNuX4ajlQK4eO3oAY/29KLQ=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.103", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lxCyedDkcen12IgBtXjkJ7iY66xa7VC4nxRNKCUeLY2ZP9hUE1AsDtbyQzqY+BQadsI/ZME9STzaHDCUFg0TpA=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.104", "", { "os": "darwin", "cpu": "arm64" }, "sha512-83Bf+LOLYCgWccAuPzftchs8LIoTzYU8f8CM7GeUFqH0VLKs2Ey+TtCTsBWpeDklOal0XFQr5begotqc/ldPVg=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.103", "", { "os": "darwin", "cpu": "x64" }, "sha512-QMYD+zUDGQliJ6m5nuNvA72jtluFeyVMoHkuA5m/Xmed/u8eLfahAKmDj3kY66ntUroPHWevcpbpvd7NCFEoFQ=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.104", "", { "os": "darwin", "cpu": "x64" }, "sha512-TAHRtEi7Gz2O3TXolPCiWpXsoapKllGCl74bOuJsyipMwfk3wuUu6SeqBPC/cmVJlp143dC8kcGLwLsd3dxsgA=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.103", "", { "os": "linux", "cpu": "arm64" }, "sha512-GzOvNr9dN6JaQ9qs7m8E75wLAHwT5CyxqkE6rEr1BO23/d2Ix7e3GYw/JRY5VnTge+eXrfDVbqNtPcQamUNiEA=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.104", "", { "os": "linux", "cpu": "arm64" }, "sha512-WJ8ssxcRpoHWgGLDYlVjlYZGkmA2G9melgXTrzk6CbwhW5tVAE6AmW5Kq9DUHclYssCpNWTvZFMDsVsf5kn2Xw=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.103", "", { "os": "linux", "cpu": "x64" }, "sha512-odywllco5zUKNc60uD3JKaCybK64u6BfmpScs4a8Qn89yH/yk23bzWXDRWaGgQdY65L2/VCbcGs1ezA1S/2YTw=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.104", "", { "os": "linux", "cpu": "x64" }, "sha512-I4DurCkJXJxxGwAq1wGZ1vzXh57GxdzljWeCJ5sIPy7coICn+/cWRw9gE9VxiWTa3T85l4YN0UFSQx7IFCEbvg=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.103", "", { "os": "win32", "cpu": "arm64" }, "sha512-tZL5w3Y0JnO7RkIvfuNDAzJn0j6+JIYl6M8DgPM5p8AQt+162S8LmbumzmqQLZl4cEev2eN7/tw72WIk6b+/CQ=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.104", "", { "os": "win32", "cpu": "arm64" }, "sha512-PDwqGHlVUiAiZa8GXiPPJVWIxucQ5+2FLcK4C76VT8HJzUXiUEGJFtuvi9oY7k29p8olcFHW75Gj1WMUrw+JKw=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.103", "", { "os": "win32", "cpu": "x64" }, "sha512-wqnibt/OE5ldSzVPxEbriA0TjI2B11CJl4uJoLxTZ47KDx7tAFIMdhBf9IRAGNCSbcDuZ8ZGEFhV+SLaftkrlw=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.104", "", { "os": "win32", "cpu": "x64" }, "sha512-fQGMxFshk/i7KskqaK5lB8u2K8Lx/LO9+PdfKu/+GBIMKmElpJXQ8Bq2sMf+7COcoXcjAjUJn8cpTyIqbFxAyQ=="], - "@opentui/solid": ["@opentui/solid@0.1.103", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.103", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-L28WFBs17Z5JXkJhPagLy8tUamkttDaaQDdb2KEO01IQ9r81yLBRpYqD/lHp6UoSISXgwQVDQ9yNtxwR1BQZvQ=="], + "@opentui/solid": ["@opentui/solid@0.1.104", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.104", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-Mma1OR3s0QZayRGdbdNv1m3H6IExRrF9QRUNWEZzDOqo3oNxTxaY9K7Qk6eGrk0unMjwwE113TSOdnG80RwYnQ=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/package.json b/package.json index b2c8a2d7a8..b8f90d9752 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "@types/cross-spawn": "6.0.6", "@octokit/rest": "22.0.0", "@hono/zod-validator": "0.4.2", - "@opentui/core": "0.1.103", - "@opentui/solid": "0.1.103", + "@opentui/core": "0.1.104", + "@opentui/solid": "0.1.104", "ulid": "3.0.1", "@kobalte/core": "0.13.11", "@types/luxon": "3.7.1", diff --git a/packages/opencode/src/cli/cmd/tui/util/index.ts b/packages/opencode/src/cli/cmd/tui/util/index.ts index a0bdbc3c28..940f18c23e 100644 --- a/packages/opencode/src/cli/cmd/tui/util/index.ts +++ b/packages/opencode/src/cli/cmd/tui/util/index.ts @@ -1,5 +1,4 @@ export * as Editor from "./editor" export * as Selection from "./selection" export * as Sound from "./sound" -export * as Terminal from "./terminal" export * as Clipboard from "./clipboard" diff --git a/packages/opencode/src/cli/cmd/tui/util/terminal.ts b/packages/opencode/src/cli/cmd/tui/util/terminal.ts deleted file mode 100644 index c026b7381c..0000000000 --- a/packages/opencode/src/cli/cmd/tui/util/terminal.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { RGBA } from "@opentui/core" - -export type Colors = Awaited> - -function parse(color: string): RGBA | null { - if (color.startsWith("rgb:")) { - const parts = color.substring(4).split("/") - return RGBA.fromInts(parseInt(parts[0], 16) >> 8, parseInt(parts[1], 16) >> 8, parseInt(parts[2], 16) >> 8, 255) - } - if (color.startsWith("#")) { - return RGBA.fromHex(color) - } - if (color.startsWith("rgb(")) { - const parts = color.substring(4, color.length - 1).split(",") - return RGBA.fromInts(parseInt(parts[0]), parseInt(parts[1]), parseInt(parts[2]), 255) - } - return null -} - -/** - * Query terminal colors including background, foreground, and palette (0-15). - * Uses OSC escape sequences to retrieve actual terminal color values. - * - * Note: OSC 4 (palette) queries may not work through tmux as responses are filtered. - * OSC 10/11 (foreground/background) typically work in most environments. - * - * Returns an object with background, foreground, and colors array. - * Any query that fails will be null/empty. - */ -export async function colors(): Promise<{ - background: RGBA | null - foreground: RGBA | null - colors: RGBA[] -}> { - if (!process.stdin.isTTY) return { background: null, foreground: null, colors: [] } - - return new Promise((resolve) => { - let background: RGBA | null = null - let foreground: RGBA | null = null - const paletteColors: RGBA[] = [] - let timeout: NodeJS.Timeout - - const cleanup = () => { - process.stdin.setRawMode(false) - process.stdin.removeListener("data", handler) - clearTimeout(timeout) - } - - const handler = (data: Buffer) => { - const str = data.toString() - - // Match OSC 11 (background color) - const bgMatch = str.match(/\x1b]11;([^\x07\x1b]+)/) - if (bgMatch) { - background = parse(bgMatch[1]) - } - - // Match OSC 10 (foreground color) - const fgMatch = str.match(/\x1b]10;([^\x07\x1b]+)/) - if (fgMatch) { - foreground = parse(fgMatch[1]) - } - - // Match OSC 4 (palette colors) - const paletteMatches = str.matchAll(/\x1b]4;(\d+);([^\x07\x1b]+)/g) - for (const match of paletteMatches) { - const index = parseInt(match[1]) - const color = parse(match[2]) - if (color) paletteColors[index] = color - } - - // Return immediately if we have all 16 palette colors - if (paletteColors.filter((c) => c !== undefined).length === 16) { - cleanup() - resolve({ background, foreground, colors: paletteColors }) - } - } - - process.stdin.setRawMode(true) - process.stdin.on("data", handler) - - // Query background (OSC 11) - process.stdout.write("\x1b]11;?\x07") - // Query foreground (OSC 10) - process.stdout.write("\x1b]10;?\x07") - // Query palette colors 0-15 (OSC 4) - for (let i = 0; i < 16; i++) { - process.stdout.write(`\x1b]4;${i};?\x07`) - } - - timeout = setTimeout(() => { - cleanup() - resolve({ background, foreground, colors: paletteColors }) - }, 1000) - }) -} diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 88f7a65f62..af13724cd7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -22,8 +22,8 @@ "zod": "catalog:" }, "peerDependencies": { - "@opentui/core": ">=0.1.103", - "@opentui/solid": ">=0.1.103" + "@opentui/core": ">=0.1.104", + "@opentui/solid": ">=0.1.104" }, "peerDependenciesMeta": { "@opentui/core": { From fad26187574c606096a5622c3731f6eadb91cea1 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 26 Apr 2026 20:45:18 +0000 Subject: [PATCH 32/34] chore: update nix node_modules hashes --- nix/hashes.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nix/hashes.json b/nix/hashes.json index 3562cf7024..8fa0e2787d 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-I5qF+zaPXIXlPWjI54XlsZ42E5EgUWbo74/ClaoJnNY=", - "aarch64-linux": "sha256-2yqFliYylXy3Lnf95nO/enefCrmP8GOfqKjnstdaMu4=", - "aarch64-darwin": "sha256-/Rk2Q6bPxOzD24HPQOe/EXlcEb7eGLGHi3P3iSqHaTU=", - "x86_64-darwin": "sha256-sKyoY9Z7858S0KG/mDhuGZLR2P7AdK+zy0xUVEJ3bt0=" + "x86_64-linux": "sha256-zQOuZkuOLm9xV5YzfcQiWSzvussXVw/dRPw4HhuYsdc=", + "aarch64-linux": "sha256-J8Ak9rCCkKlBj0KhdUOklglpR3ntRP2VjtdmEJox6Ro=", + "aarch64-darwin": "sha256-r4F6S9CfGR6qxSaSqu9AIjWCEKd/PuT1Wprcbzk0IwY=", + "x86_64-darwin": "sha256-I/ztb7VnLbJYzAxf2egN0+YsJcP5yix3tIoHtiuRUXE=" } } From af3998c8a60dc00f27c94a410c4f508e5b0891a7 Mon Sep 17 00:00:00 2001 From: opencode Date: Sun, 26 Apr 2026 21:01:16 +0000 Subject: [PATCH 33/34] sync release versions for v1.14.26 --- bun.lock | 32 +++++++++++++------------- packages/app/package.json | 2 +- 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/core/package.json | 2 +- packages/desktop-electron/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/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index 8a3684c0b0..c5eff531f3 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/core": "workspace:*", @@ -83,7 +83,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -117,7 +117,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -144,7 +144,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/openai": "3.0.48", @@ -168,7 +168,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -192,7 +192,7 @@ }, "packages/core": { "name": "@opencode-ai/core", - "version": "1.14.25", + "version": "1.14.26", "bin": { "opencode": "./bin/opencode", }, @@ -226,7 +226,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -259,7 +259,7 @@ }, "packages/desktop-electron": { "name": "@opencode-ai/desktop-electron", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "drizzle-orm": "catalog:", "effect": "catalog:", @@ -303,7 +303,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@opencode-ai/core": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -332,7 +332,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -348,7 +348,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.14.25", + "version": "1.14.26", "bin": { "opencode": "./bin/opencode", }, @@ -491,7 +491,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@opencode-ai/sdk": "workspace:*", "effect": "catalog:", @@ -526,7 +526,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "cross-spawn": "catalog:", }, @@ -541,7 +541,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -576,7 +576,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/core": "workspace:*", @@ -625,7 +625,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index f9d8150ba2..d5349a4a49 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.14.25", + "version": "1.14.26", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 5abd192568..2572ba1757 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 31f4f9a0ae..67f6dad754 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.14.25", + "version": "1.14.26", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 4f30ea99b4..2f02393dd0 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.14.25", + "version": "1.14.26", "$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 39556c7d39..77603c4cb3 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.14.25", + "version": "1.14.26", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/core/package.json b/packages/core/package.json index 62d56908cb..d69f1b575d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.14.25", + "version": "1.14.26", "name": "@opencode-ai/core", "type": "module", "license": "MIT", diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index f382ad16dd..69bef5dff4 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop-electron", "private": true, - "version": "1.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "homepage": "https://opencode.ai", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 242bd19718..8baca7e90c 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index a5a2997b95..93c3f15f18 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.14.25", + "version": "1.14.26", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 6deaee2201..fee6803c48 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.14.25" +version = "1.14.26" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.25/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.26/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 6c1428ad5a..dc4ff19016 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.14.25", + "version": "1.14.26", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index df0043b066..72032b3e1c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.14.25", + "version": "1.14.26", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index af13724cd7..edd615a25f 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.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 3f6ffa7a5a..12045fba28 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.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 9ab39fad7d..0e2db047d5 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index da7a0f6732..e5e8bad61b 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.14.25", + "version": "1.14.26", "type": "module", "license": "MIT", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index ce4a80436b..8c26f31d42 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.14.25", + "version": "1.14.26", "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 73c6cc9fb9..df4a484cbb 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.14.25", + "version": "1.14.26", "publisher": "sst-dev", "repository": { "type": "git", From c68907ece239819a6fdd31f6b2463fb69fbc5b37 Mon Sep 17 00:00:00 2001 From: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Date: Mon, 27 Apr 2026 00:27:02 +0200 Subject: [PATCH 34/34] fix(tui): update toast duration handling to use default value (#23395) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/cli/cmd/tui/event.ts | 8 ++++++-- packages/opencode/src/cli/cmd/tui/ui/toast.tsx | 13 ++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/event.ts b/packages/opencode/src/cli/cmd/tui/event.ts index ab85b1e645..1c764c12fe 100644 --- a/packages/opencode/src/cli/cmd/tui/event.ts +++ b/packages/opencode/src/cli/cmd/tui/event.ts @@ -1,6 +1,8 @@ import { BusEvent } from "@/bus/bus-event" import { SessionID } from "@/session/schema" -import { Schema } from "effect" +import { Effect, Schema } from "effect" + +const DEFAULT_TOAST_DURATION = 5000 export const TuiEvent = { PromptAppend: BusEvent.define("tui.prompt.append", Schema.Struct({ text: Schema.String })), @@ -36,7 +38,9 @@ export const TuiEvent = { title: Schema.optional(Schema.String), message: Schema.String, variant: Schema.Literals(["info", "success", "warning", "error"]), - duration: Schema.optional(Schema.Number).annotate({ description: "Duration in milliseconds" }), + duration: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({ + description: "Duration in milliseconds", + }), }), ), SessionSelect: BusEvent.define( diff --git a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx index 69674ba7ce..d15fb39205 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/toast.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/toast.tsx @@ -5,10 +5,13 @@ import { useTerminalDimensions } from "@opentui/solid" import { SplitBorder } from "../component/border" import { TextAttributes } from "@opentui/core" import { Schema } from "effect" -import { type TuiEvent } from "../event" +import { TuiEvent } from "../event" +type ToastInput = Schema.Codec.Encoded export type ToastOptions = Schema.Schema.Type +const decodeToastOptions = Schema.decodeUnknownSync(TuiEvent.ToastShow.properties) + export function Toast() { const toast = useToast() const { theme } = useTheme() @@ -55,13 +58,13 @@ function init() { let timeoutHandle: NodeJS.Timeout | null = null const toast = { - show(options: ToastOptions) { - const { duration, ...currentToast } = options - setStore("currentToast", currentToast) + show(options: ToastInput) { + const toastOptions = decodeToastOptions(options) + setStore("currentToast", toastOptions) if (timeoutHandle) clearTimeout(timeoutHandle) timeoutHandle = setTimeout(() => { setStore("currentToast", null) - }, duration).unref() + }, toastOptions.duration).unref() }, error: (err: any) => { if (err instanceof Error)