Просмотр исходного кода

Merge branch 'dev' of https://github.com/sst/opencode into dev

David Hill 2 месяцев назад
Родитель
Сommit
e91d121ef8
95 измененных файлов с 517 добавлено и 332 удалено
  1. 1 0
      .github/workflows/opencode.yml
  2. 1 1
      .github/workflows/publish.yml
  3. 1 14
      .opencode/opencode.jsonc
  4. 16 15
      bun.lock
  5. 3 3
      infra/enterprise.ts
  6. 6 0
      infra/stage.ts
  7. 1 1
      nix/hashes.json
  8. 1 1
      package.json
  9. 1 1
      packages/console/app/package.json
  10. 20 0
      packages/console/app/src/routes/t/[...path].tsx
  11. 1 1
      packages/console/core/package.json
  12. 1 1
      packages/console/function/package.json
  13. 1 1
      packages/console/mail/package.json
  14. 1 1
      packages/desktop/package.json
  15. 2 2
      packages/desktop/tsconfig.json
  16. 2 1
      packages/enterprise/package.json
  17. 0 2
      packages/enterprise/src/entry-server.tsx
  18. 3 0
      packages/enterprise/src/routes/index.tsx
  19. 246 209
      packages/enterprise/src/routes/share/[shareID].tsx
  20. 8 1
      packages/enterprise/vite.config.ts
  21. 6 6
      packages/extensions/zed/extension.toml
  22. 1 1
      packages/function/package.json
  23. 1 1
      packages/opencode/package.json
  24. 8 9
      packages/opencode/src/bus/index.ts
  25. 1 1
      packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
  26. 10 0
      packages/opencode/src/project/instance.ts
  27. 10 0
      packages/opencode/src/provider/provider.ts
  28. 9 2
      packages/opencode/src/provider/transform.ts
  29. 30 0
      packages/opencode/src/server/server.ts
  30. 2 23
      packages/opencode/src/session/index.ts
  31. 7 8
      packages/opencode/src/share/share-next.ts
  32. 1 2
      packages/opencode/src/tool/bash.txt
  33. 1 1
      packages/plugin/package.json
  34. 1 1
      packages/sdk/js/package.json
  35. 13 0
      packages/sdk/js/src/v2/gen/sdk.gen.ts
  36. 24 0
      packages/sdk/js/src/v2/gen/types.gen.ts
  37. 42 0
      packages/sdk/openapi.json
  38. 1 1
      packages/slack/package.json
  39. 1 1
      packages/tauri/package.json
  40. BIN
      packages/tauri/src-tauri/icons/128x128.png
  41. BIN
      packages/tauri/src-tauri/icons/[email protected]
  42. BIN
      packages/tauri/src-tauri/icons/32x32.png
  43. BIN
      packages/tauri/src-tauri/icons/64x64.png
  44. BIN
      packages/tauri/src-tauri/icons/Square107x107Logo.png
  45. BIN
      packages/tauri/src-tauri/icons/Square142x142Logo.png
  46. BIN
      packages/tauri/src-tauri/icons/Square150x150Logo.png
  47. BIN
      packages/tauri/src-tauri/icons/Square284x284Logo.png
  48. BIN
      packages/tauri/src-tauri/icons/Square30x30Logo.png
  49. BIN
      packages/tauri/src-tauri/icons/Square310x310Logo.png
  50. BIN
      packages/tauri/src-tauri/icons/Square44x44Logo.png
  51. BIN
      packages/tauri/src-tauri/icons/Square71x71Logo.png
  52. BIN
      packages/tauri/src-tauri/icons/Square89x89Logo.png
  53. BIN
      packages/tauri/src-tauri/icons/StoreLogo.png
  54. BIN
      packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
  55. BIN
      packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png
  56. BIN
      packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png
  57. BIN
      packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
  58. BIN
      packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png
  59. BIN
      packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png
  60. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
  61. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png
  62. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png
  63. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
  64. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png
  65. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png
  66. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
  67. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png
  68. BIN
      packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png
  69. BIN
      packages/tauri/src-tauri/icons/icon.icns
  70. BIN
      packages/tauri/src-tauri/icons/icon.ico
  71. BIN
      packages/tauri/src-tauri/icons/icon.png
  72. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  73. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  74. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  75. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  76. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  77. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  78. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  79. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  80. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  81. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  82. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  83. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  84. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  85. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  86. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  87. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  88. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  89. BIN
      packages/tauri/src-tauri/icons/ios/[email protected]
  90. 14 2
      packages/tauri/tsconfig.json
  91. 1 1
      packages/ui/package.json
  92. 1 1
      packages/util/package.json
  93. 1 1
      packages/web/package.json
  94. 14 14
      packages/web/src/content/docs/sdk.mdx
  95. 1 1
      sdks/vscode/package.json

+ 1 - 0
.github/workflows/opencode.yml

@@ -29,5 +29,6 @@ jobs:
         uses: sst/opencode/github@latest
         env:
           OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+          OPENCODE_PERMISSION: '{"bash": "deny"}'
         with:
           model: opencode/claude-haiku-4-5

+ 1 - 1
.github/workflows/publish.yml

@@ -55,7 +55,7 @@ jobs:
 
       - name: Install OpenCode
         if: inputs.bump || inputs.version
-        run: curl -fsSL https://opencode.ai/install | bash
+        run: bun i -g [email protected]
 
       - name: Login to GitHub Container Registry
         uses: docker/login-action@v3

+ 1 - 14
.opencode/opencode.jsonc

@@ -1,6 +1,6 @@
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["opencode-openai-codex-auth"],
+  // "plugin": ["opencode-openai-codex-auth"],
   // "enterprise": {
   //   "url": "https://enterprise.dev.opencode.ai",
   // },
@@ -10,17 +10,4 @@
       "options": {},
     },
   },
-  "mcp": {
-    "exa": {
-      "type": "remote",
-      "url": "https://mcp.exa.ai/mcp",
-    },
-    "morph": {
-      "type": "local",
-      "command": ["bunx", "@morphllm/morphmcp"],
-      "environment": {
-        "ENABLED_TOOLS": "warp_grep",
-      },
-    },
-  },
 }

+ 16 - 15
bun.lock

@@ -20,7 +20,7 @@
     },
     "packages/console/app": {
       "name": "@opencode-ai/console-app",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@cloudflare/vite-plugin": "1.15.2",
         "@ibm/plex": "6.4.1",
@@ -48,7 +48,7 @@
     },
     "packages/console/core": {
       "name": "@opencode-ai/console-core",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@aws-sdk/client-sts": "3.782.0",
         "@jsx-email/render": "1.1.1",
@@ -75,7 +75,7 @@
     },
     "packages/console/function": {
       "name": "@opencode-ai/console-function",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@ai-sdk/anthropic": "2.0.0",
         "@ai-sdk/openai": "2.0.2",
@@ -99,7 +99,7 @@
     },
     "packages/console/mail": {
       "name": "@opencode-ai/console-mail",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@jsx-email/all": "2.2.3",
         "@jsx-email/cli": "1.4.3",
@@ -123,7 +123,7 @@
     },
     "packages/desktop": {
       "name": "@opencode-ai/desktop",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@opencode-ai/sdk": "workspace:*",
@@ -168,7 +168,7 @@
     },
     "packages/enterprise": {
       "name": "@opencode-ai/enterprise",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/util": "workspace:*",
@@ -179,6 +179,7 @@
         "aws4fetch": "^1.0.20",
         "hono": "catalog:",
         "hono-openapi": "catalog:",
+        "js-base64": "3.7.7",
         "luxon": "catalog:",
         "nitro": "3.0.1-alpha.1",
         "solid-js": "catalog:",
@@ -196,7 +197,7 @@
     },
     "packages/function": {
       "name": "@opencode-ai/function",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@octokit/auth-app": "8.0.1",
         "@octokit/rest": "22.0.0",
@@ -212,7 +213,7 @@
     },
     "packages/opencode": {
       "name": "opencode",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "bin": {
         "opencode": "./bin/opencode",
       },
@@ -304,7 +305,7 @@
     },
     "packages/plugin": {
       "name": "@opencode-ai/plugin",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "zod": "catalog:",
@@ -324,7 +325,7 @@
     },
     "packages/sdk/js": {
       "name": "@opencode-ai/sdk",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "devDependencies": {
         "@hey-api/openapi-ts": "0.88.1",
         "@tsconfig/node22": "catalog:",
@@ -335,7 +336,7 @@
     },
     "packages/slack": {
       "name": "@opencode-ai/slack",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "@slack/bolt": "^3.17.1",
@@ -348,7 +349,7 @@
     },
     "packages/tauri": {
       "name": "@opencode-ai/tauri",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@opencode-ai/desktop": "workspace:*",
         "@tauri-apps/api": "^2",
@@ -370,7 +371,7 @@
     },
     "packages/ui": {
       "name": "@opencode-ai/ui",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@opencode-ai/sdk": "workspace:*",
@@ -402,7 +403,7 @@
     },
     "packages/util": {
       "name": "@opencode-ai/util",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "zod": "catalog:",
       },
@@ -413,7 +414,7 @@
     },
     "packages/web": {
       "name": "@opencode-ai/web",
-      "version": "1.0.143",
+      "version": "1.0.146",
       "dependencies": {
         "@astrojs/cloudflare": "12.6.3",
         "@astrojs/markdown-remark": "6.3.1",

+ 3 - 3
infra/enterprise.ts

@@ -1,10 +1,10 @@
 import { SECRET } from "./secret"
-import { domain } from "./stage"
+import { domain, shortDomain } from "./stage"
 
 const storage = new sst.cloudflare.Bucket("EnterpriseStorage")
 
-const enterprise = new sst.cloudflare.x.SolidStart("Enterprise", {
-  domain: "enterprise." + domain,
+const teams = new sst.cloudflare.x.SolidStart("Teams", {
+  domain: shortDomain,
   path: "packages/enterprise",
   buildCommand: "bun run build:cloudflare",
   environment: {

+ 6 - 0
infra/stage.ts

@@ -11,3 +11,9 @@ new cloudflare.RegionalHostname("RegionalHostname", {
   regionKey: "us",
   zoneId: zoneID,
 })
+
+export const shortDomain = (() => {
+  if ($app.stage === "production") return "opncd.ai"
+  if ($app.stage === "dev") return "dev.opncd.ai"
+  return `${$app.stage}.dev.opncd.ai`
+})()

+ 1 - 1
nix/hashes.json

@@ -1,3 +1,3 @@
 {
-  "nodeModules": "sha256-WQMQmqKojxdRtwv6KL9HBaDfwYa4qPn2pvXKqgNM73A="
+  "nodeModules": "sha256-WcFRAG8w1XwgZxzyAawkQ7mYfJxu5VhA3sPApEG6FdI="
 }

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
   "description": "AI-powered development tool",
   "private": true,
   "type": "module",
-  "packageManager": "[email protected].4",
+  "packageManager": "[email protected].3",
   "scripts": {
     "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
     "typecheck": "bun turbo typecheck",

+ 1 - 1
packages/console/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-app",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "scripts": {
     "typecheck": "tsgo --noEmit",

+ 20 - 0
packages/console/app/src/routes/t/[...path].tsx

@@ -0,0 +1,20 @@
+import type { APIEvent } from "@solidjs/start/server"
+
+async function handler(evt: APIEvent) {
+  const req = evt.request.clone()
+  const url = new URL(req.url)
+  const targetUrl = `https://enterprise.opencode.ai/${url.pathname}${url.search}`
+  const response = await fetch(targetUrl, {
+    method: req.method,
+    headers: req.headers,
+    body: req.body,
+  })
+  return response
+}
+
+export const GET = handler
+export const POST = handler
+export const PUT = handler
+export const DELETE = handler
+export const OPTIONS = handler
+export const PATCH = handler

+ 1 - 1
packages/console/core/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/console-core",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "private": true,
   "type": "module",
   "dependencies": {

+ 1 - 1
packages/console/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-function",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 1 - 1
packages/console/mail/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/console-mail",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "dependencies": {
     "@jsx-email/all": "2.2.3",
     "@jsx-email/cli": "1.4.3",

+ 1 - 1
packages/desktop/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/desktop",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "description": "",
   "type": "module",
   "exports": {

+ 2 - 2
packages/desktop/tsconfig.json

@@ -14,11 +14,11 @@
     "strict": true,
     "noEmit": false,
     "emitDeclarationOnly": true,
-    "outDir": "ts-dist",
+    "outDir": "node_modules/.ts-dist",
     "isolatedModules": true,
     "paths": {
       "@/*": ["./src/*"]
     }
   },
-  "exclude": ["dist"]
+  "exclude": ["dist", "ts-dist"]
 }

+ 2 - 1
packages/enterprise/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/enterprise",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "private": true,
   "type": "module",
   "scripts": {
@@ -20,6 +20,7 @@
     "@solidjs/meta": "catalog:",
     "hono": "catalog:",
     "hono-openapi": "catalog:",
+    "js-base64": "3.7.7",
     "luxon": "catalog:",
     "nitro": "3.0.1-alpha.1",
     "solid-js": "catalog:",

+ 0 - 2
packages/enterprise/src/entry-server.tsx

@@ -11,8 +11,6 @@ export default createHandler(() => (
           <title>OpenCode</title>
           <meta name="theme-color" content="#F8F7F7" />
           <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
-          <meta property="og:image" content="/social-share.png" />
-          <meta property="twitter:image" content="/social-share.png" />
           {assets}
         </head>
         <body class="antialiased overscroll-none select-none text-12-regular">

+ 3 - 0
packages/enterprise/src/routes/index.tsx

@@ -0,0 +1,3 @@
+export default function () {
+  return <div>Hello World</div>
+}

+ 246 - 209
packages/enterprise/src/routes/share/[shareID].tsx

@@ -23,6 +23,8 @@ import { preloadMultiFileDiff, PreloadMultiFileDiffResult } from "@pierre/precis
 import { Diff as SSRDiff } from "@opencode-ai/ui/diff-ssr"
 import { clientOnly } from "@solidjs/start"
 import { type IconName } from "@opencode-ai/ui/icons/provider"
+import { Meta } from "@solidjs/meta"
+import { Base64 } from "js-base64"
 
 const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff })))
 
@@ -41,6 +43,7 @@ const getData = query(async (shareID) => {
   const data = await Share.data(shareID)
   const result: {
     sessionID: string
+    shareID: string
     session: Session[]
     session_diff: {
       [sessionID: string]: FileDiff[]
@@ -65,6 +68,7 @@ const getData = query(async (shareID) => {
     }
   } = {
     sessionID: share.sessionID,
+    shareID,
     session: [],
     session_diff: {
       [share.sessionID]: [],
@@ -153,244 +157,277 @@ export default function () {
         )
       }}
     >
+      <Meta name="robots" content="noindex, nofollow" />
       <Show when={data()}>
         {(data) => {
           const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id))
           if (!match().found) throw new Error(`Session ${data().sessionID} not found`)
           const info = createMemo(() => data().session[match().index])
+          const ogImage = createMemo(() => {
+            const models = new Set<string>()
+            const messages = data().message[data().sessionID] ?? []
+            for (const msg of messages) {
+              if (msg.role === "assistant" && msg.modelID) {
+                models.add(msg.modelID)
+              }
+            }
+            const modelIDs = Array.from(models)
+            const encodedTitle = encodeURIComponent(Base64.encode(encodeURIComponent(info().title.substring(0, 700))))
+            let modelParam: string
+            if (modelIDs.length === 1) {
+              modelParam = modelIDs[0]
+            } else if (modelIDs.length === 2) {
+              modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs[1]}`)
+            } else if (modelIDs.length > 2) {
+              modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs.length - 1} others`)
+            } else {
+              modelParam = "unknown"
+            }
+            const version = `v${info().version}`
+            return `https://social-cards.sst.dev/opencode-share/${encodedTitle}.png?model=${modelParam}&version=${version}&id=${data().shareID}`
+          })
 
           return (
-            <DiffComponentProvider component={ClientOnlyDiff}>
-              <DataProvider data={data()} directory={info().directory}>
-                {iife(() => {
-                  const [store, setStore] = createStore({
-                    messageId: undefined as string | undefined,
-                  })
-                  const messages = createMemo(() =>
-                    data().sessionID
-                      ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort(
-                          (a, b) => b.time.created - a.time.created,
-                        )
-                      : [],
-                  )
-                  const firstUserMessage = createMemo(() => messages().at(0))
-                  const activeMessage = createMemo(
-                    () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
-                  )
-                  function setActiveMessage(message: UserMessage | undefined) {
-                    if (message) {
-                      setStore("messageId", message.id)
-                    } else {
-                      setStore("messageId", undefined)
+            <>
+              <Meta name="description" content="opencode - The AI coding agent built for the terminal." />
+              <Meta property="og:image" content={ogImage()} />
+              <Meta name="twitter:image" content={ogImage()} />
+              <DiffComponentProvider component={ClientOnlyDiff}>
+                <DataProvider data={data()} directory={info().directory}>
+                  {iife(() => {
+                    const [store, setStore] = createStore({
+                      messageId: undefined as string | undefined,
+                    })
+                    const messages = createMemo(() =>
+                      data().sessionID
+                        ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort(
+                            (a, b) => b.time.created - a.time.created,
+                          )
+                        : [],
+                    )
+                    const firstUserMessage = createMemo(() => messages().at(0))
+                    const activeMessage = createMemo(
+                      () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(),
+                    )
+                    function setActiveMessage(message: UserMessage | undefined) {
+                      if (message) {
+                        setStore("messageId", message.id)
+                      } else {
+                        setStore("messageId", undefined)
+                      }
                     }
-                  }
-                  const provider = createMemo(() => activeMessage()?.model?.providerID)
-                  const modelID = createMemo(() => activeMessage()?.model?.modelID)
-                  const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID()))
-                  const diffs = createMemo(() => {
-                    const diffs = data().session_diff[data().sessionID] ?? []
-                    const preloaded = data().session_diff_preload[data().sessionID] ?? []
-                    return diffs.map((diff) => ({
-                      ...diff,
-                      preloaded: preloaded.find((d) => d.newFile.name === diff.file),
-                    }))
-                  })
-                  const splitDiffs = createMemo(() => {
-                    const diffs = data().session_diff[data().sessionID] ?? []
-                    const preloaded = data().session_diff_preload_split[data().sessionID] ?? []
-                    return diffs.map((diff) => ({
-                      ...diff,
-                      preloaded: preloaded.find((d) => d.newFile.name === diff.file),
-                    }))
-                  })
+                    const provider = createMemo(() => activeMessage()?.model?.providerID)
+                    const modelID = createMemo(() => activeMessage()?.model?.modelID)
+                    const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID()))
+                    const diffs = createMemo(() => {
+                      const diffs = data().session_diff[data().sessionID] ?? []
+                      const preloaded = data().session_diff_preload[data().sessionID] ?? []
+                      return diffs.map((diff) => ({
+                        ...diff,
+                        preloaded: preloaded.find((d) => d.newFile.name === diff.file),
+                      }))
+                    })
+                    const splitDiffs = createMemo(() => {
+                      const diffs = data().session_diff[data().sessionID] ?? []
+                      const preloaded = data().session_diff_preload_split[data().sessionID] ?? []
+                      return diffs.map((diff) => ({
+                        ...diff,
+                        preloaded: preloaded.find((d) => d.newFile.name === diff.file),
+                      }))
+                    })
 
-                  const title = () => (
-                    <div class="flex flex-col gap-4">
-                      <div class="h-8 flex gap-4 items-center justify-start self-stretch">
-                        <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
-                          <Mark class="shrink-0 w-3 my-0.5" />
-                          <div class="text-12-mono text-text-base">v{info().version}</div>
-                        </div>
-                        <div class="flex gap-2 items-center">
-                          <ProviderIcon id={provider() as IconName} class="size-3.5 shrink-0 text-icon-strong-base" />
-                          <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
-                        </div>
-                        <div class="text-12-regular text-text-weaker">
-                          {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
+                    const title = () => (
+                      <div class="flex flex-col gap-4">
+                        <div class="h-8 flex gap-4 items-center justify-start self-stretch">
+                          <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
+                            <Mark class="shrink-0 w-3 my-0.5" />
+                            <div class="text-12-mono text-text-base">v{info().version}</div>
+                          </div>
+                          <div class="flex gap-2 items-center">
+                            <ProviderIcon id={provider() as IconName} class="size-3.5 shrink-0 text-icon-strong-base" />
+                            <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
+                          </div>
+                          <div class="text-12-regular text-text-weaker">
+                            {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
+                          </div>
                         </div>
+                        <div class="text-left text-16-medium text-text-strong">{info().title}</div>
                       </div>
-                      <div class="text-left text-16-medium text-text-strong">{info().title}</div>
-                    </div>
-                  )
+                    )
 
-                  const turns = () => (
-                    <div class="relative mt-2 pt-6 pb-8 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
-                      <div class="px-4">{title()}</div>
-                      <div class="flex flex-col gap-15 items-start justify-start mt-4">
-                        <For each={messages()}>
-                          {(message) => (
-                            <SessionTurn
-                              sessionID={data().sessionID}
-                              messageID={message.id}
-                              classes={{
-                                root: "min-w-0 w-full relative",
-                                content:
-                                  "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
-                                container: "px-4",
-                              }}
-                            />
-                          )}
-                        </For>
-                      </div>
-                      <div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0">
-                        <Logo class="w-58.5 opacity-12" />
+                    const turns = () => (
+                      <div class="relative mt-2 pt-6 pb-8 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
+                        <div class="px-4">{title()}</div>
+                        <div class="flex flex-col gap-15 items-start justify-start mt-4">
+                          <For each={messages()}>
+                            {(message) => (
+                              <SessionTurn
+                                sessionID={data().sessionID}
+                                messageID={message.id}
+                                classes={{
+                                  root: "min-w-0 w-full relative",
+                                  content:
+                                    "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
+                                  container: "px-4",
+                                }}
+                              />
+                            )}
+                          </For>
+                        </div>
+                        <div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0">
+                          <Logo class="w-58.5 opacity-12" />
+                        </div>
                       </div>
-                    </div>
-                  )
+                    )
 
-                  const wide = createMemo(() => diffs().length === 0)
+                    const wide = createMemo(() => diffs().length === 0)
 
-                  return (
-                    <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
-                      <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
-                        <div class="">
-                          <a href="https://opencode.ai">
-                            <Mark />
-                          </a>
-                        </div>
-                        <div class="flex gap-3 items-center">
-                          <IconButton
-                            as={"a"}
-                            href="https://github.com/sst/opencode"
-                            target="_blank"
-                            icon="github"
-                            variant="ghost"
-                          />
-                          <IconButton
-                            as={"a"}
-                            href="https://opencode.ai/discord"
-                            target="_blank"
-                            icon="discord"
-                            variant="ghost"
-                          />
-                        </div>
-                      </header>
-                      <div class="select-text flex flex-col flex-1 min-h-0">
-                        <div
-                          classList={{ "hidden w-full flex-1 min-h-0": true, "md:flex": wide(), "lg:flex": !wide() }}
-                        >
+                    return (
+                      <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
+                        <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base">
+                          <div class="">
+                            <a href="https://opencode.ai">
+                              <Mark />
+                            </a>
+                          </div>
+                          <div class="flex gap-3 items-center">
+                            <IconButton
+                              as={"a"}
+                              href="https://github.com/sst/opencode"
+                              target="_blank"
+                              icon="github"
+                              variant="ghost"
+                            />
+                            <IconButton
+                              as={"a"}
+                              href="https://opencode.ai/discord"
+                              target="_blank"
+                              icon="discord"
+                              variant="ghost"
+                            />
+                          </div>
+                        </header>
+                        <div class="select-text flex flex-col flex-1 min-h-0">
                           <div
-                            classList={{
-                              "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true,
-                              "mx-auto max-w-146": !wide(),
-                            }}
+                            classList={{ "hidden w-full flex-1 min-h-0": true, "md:flex": wide(), "lg:flex": !wide() }}
                           >
                             <div
                               classList={{
-                                "w-full flex justify-start items-start min-w-0": true,
-                                "max-w-146 mx-auto px-6": wide(),
-                                "pr-6 pl-18": !wide() && messages().length > 1,
-                                "px-6": !wide() && messages().length === 1,
+                                "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true,
+                                "mx-auto max-w-146": !wide(),
                               }}
                             >
-                              {title()}
-                            </div>
-                            <div class="flex items-start justify-start h-full min-h-0">
-                              <SessionMessageRail
-                                messages={messages()}
-                                current={activeMessage()}
-                                onMessageSelect={setActiveMessage}
-                                wide={wide()}
-                              />
-                              <SessionTurn
-                                sessionID={data().sessionID}
-                                messageID={store.messageId ?? firstUserMessage()!.id!}
-                                classes={{
-                                  root: "grow",
-                                  content: "flex flex-col justify-between items-start",
-                                  container:
-                                    "w-full pb-20 " +
-                                    (wide() ? "max-w-146 mx-auto px-6" : messages().length > 1 ? "pr-6 pl-18" : "px-6"),
+                              <div
+                                classList={{
+                                  "w-full flex justify-start items-start min-w-0": true,
+                                  "max-w-146 mx-auto px-6": wide(),
+                                  "pr-6 pl-18": !wide() && messages().length > 1,
+                                  "px-6": !wide() && messages().length === 1,
                                 }}
                               >
-                                <div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
-                                  <Logo class="w-58.5 opacity-12" />
-                                </div>
-                              </SessionTurn>
-                            </div>
-                          </div>
-                          <Show when={diffs().length > 0}>
-                            <DiffComponentProvider component={SSRDiff}>
-                              <div class="@container relative grow pt-14 flex-1 min-h-0 border-l border-border-weak-base">
-                                <SessionReview
-                                  class="@4xl:hidden"
-                                  diffs={diffs()}
-                                  classes={{
-                                    root: "pb-20",
-                                    header: "px-6",
-                                    container: "px-6",
-                                  }}
+                                {title()}
+                              </div>
+                              <div class="flex items-start justify-start h-full min-h-0">
+                                <SessionMessageRail
+                                  messages={messages()}
+                                  current={activeMessage()}
+                                  onMessageSelect={setActiveMessage}
+                                  wide={wide()}
                                 />
-                                <SessionReview
-                                  split
-                                  class="hidden @4xl:flex"
-                                  diffs={splitDiffs()}
+                                <SessionTurn
+                                  sessionID={data().sessionID}
+                                  messageID={store.messageId ?? firstUserMessage()!.id!}
                                   classes={{
-                                    root: "pb-20",
-                                    header: "px-6",
-                                    container: "px-6",
+                                    root: "grow",
+                                    content: "flex flex-col justify-between items-start",
+                                    container:
+                                      "w-full pb-20 " +
+                                      (wide()
+                                        ? "max-w-146 mx-auto px-6"
+                                        : messages().length > 1
+                                          ? "pr-6 pl-18"
+                                          : "px-6"),
                                   }}
-                                />
+                                >
+                                  <div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
+                                    <Logo class="w-58.5 opacity-12" />
+                                  </div>
+                                </SessionTurn>
                               </div>
-                            </DiffComponentProvider>
-                          </Show>
-                        </div>
-                        <Switch>
-                          <Match when={diffs().length > 0}>
-                            <Tabs classList={{ "md:hidden": wide(), "lg:hidden": !wide() }}>
-                              <Tabs.List>
-                                <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
-                                  Session
-                                </Tabs.Trigger>
-                                <Tabs.Trigger value="review" class="w-1/2 !border-r-0" classes={{ button: "w-full" }}>
-                                  {diffs().length} Files Changed
-                                </Tabs.Trigger>
-                              </Tabs.List>
-                              <Tabs.Content value="session" class="!overflow-hidden">
-                                {turns()}
-                              </Tabs.Content>
-                              <Tabs.Content
-                                forceMount
-                                value="review"
-                                class="!overflow-hidden hidden data-[selected]:block"
-                              >
-                                <div class="relative h-full pt-8 overflow-y-auto no-scrollbar">
-                                  <DiffComponentProvider component={SSRDiff}>
-                                    <SessionReview
-                                      diffs={diffs()}
-                                      classes={{
-                                        root: "pb-20",
-                                        header: "px-4",
-                                        container: "px-4",
-                                      }}
-                                    />
-                                  </DiffComponentProvider>
-                                </div>
-                              </Tabs.Content>
-                            </Tabs>
-                          </Match>
-                          <Match when={true}>
-                            <div classList={{ "!overflow-hidden": true, "md:hidden": wide(), "lg:hidden": !wide() }}>
-                              {turns()}
                             </div>
-                          </Match>
-                        </Switch>
+                            <Show when={diffs().length > 0}>
+                              <DiffComponentProvider component={SSRDiff}>
+                                <div class="@container relative grow pt-14 flex-1 min-h-0 border-l border-border-weak-base">
+                                  <SessionReview
+                                    class="@4xl:hidden"
+                                    diffs={diffs()}
+                                    classes={{
+                                      root: "pb-20",
+                                      header: "px-6",
+                                      container: "px-6",
+                                    }}
+                                  />
+                                  <SessionReview
+                                    split
+                                    class="hidden @4xl:flex"
+                                    diffs={splitDiffs()}
+                                    classes={{
+                                      root: "pb-20",
+                                      header: "px-6",
+                                      container: "px-6",
+                                    }}
+                                  />
+                                </div>
+                              </DiffComponentProvider>
+                            </Show>
+                          </div>
+                          <Switch>
+                            <Match when={diffs().length > 0}>
+                              <Tabs classList={{ "md:hidden": wide(), "lg:hidden": !wide() }}>
+                                <Tabs.List>
+                                  <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
+                                    Session
+                                  </Tabs.Trigger>
+                                  <Tabs.Trigger value="review" class="w-1/2 !border-r-0" classes={{ button: "w-full" }}>
+                                    {diffs().length} Files Changed
+                                  </Tabs.Trigger>
+                                </Tabs.List>
+                                <Tabs.Content value="session" class="!overflow-hidden">
+                                  {turns()}
+                                </Tabs.Content>
+                                <Tabs.Content
+                                  forceMount
+                                  value="review"
+                                  class="!overflow-hidden hidden data-[selected]:block"
+                                >
+                                  <div class="relative h-full pt-8 overflow-y-auto no-scrollbar">
+                                    <DiffComponentProvider component={SSRDiff}>
+                                      <SessionReview
+                                        diffs={diffs()}
+                                        classes={{
+                                          root: "pb-20",
+                                          header: "px-4",
+                                          container: "px-4",
+                                        }}
+                                      />
+                                    </DiffComponentProvider>
+                                  </div>
+                                </Tabs.Content>
+                              </Tabs>
+                            </Match>
+                            <Match when={true}>
+                              <div classList={{ "!overflow-hidden": true, "md:hidden": wide(), "lg:hidden": !wide() }}>
+                                {turns()}
+                              </div>
+                            </Match>
+                          </Switch>
+                        </div>
                       </div>
-                    </div>
-                  )
-                })}
-              </DataProvider>
-            </DiffComponentProvider>
+                    )
+                  })}
+                </DataProvider>
+              </DiffComponentProvider>
+            </>
           )
         }}
       </Show>

+ 8 - 1
packages/enterprise/vite.config.ts

@@ -18,7 +18,14 @@ const nitroConfig: any = (() => {
 })()
 
 export default defineConfig({
-  plugins: [tailwindcss(), solidStart() as PluginOption, nitro(nitroConfig)],
+  plugins: [
+    tailwindcss(),
+    solidStart() as PluginOption,
+    nitro({
+      ...nitroConfig,
+      baseURL: process.env.OPENCODE_BASE_URL,
+    }),
+  ],
   server: {
     host: "0.0.0.0",
     allowedHosts: true,

+ 6 - 6
packages/extensions/zed/extension.toml

@@ -1,7 +1,7 @@
 id = "opencode"
 name = "OpenCode"
 description = "The open source coding agent."
-version = "1.0.143"
+version = "1.0.146"
 schema_version = 1
 authors = ["Anomaly"]
 repository = "https://github.com/sst/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
 icon = "./icons/opencode.svg"
 
 [agent_servers.opencode.targets.darwin-aarch64]
-archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-darwin-arm64.zip"
+archive = "https://github.com/sst/opencode/releases/download/v1.0.146/opencode-darwin-arm64.zip"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.darwin-x86_64]
-archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-darwin-x64.zip"
+archive = "https://github.com/sst/opencode/releases/download/v1.0.146/opencode-darwin-x64.zip"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.linux-aarch64]
-archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-linux-arm64.tar.gz"
+archive = "https://github.com/sst/opencode/releases/download/v1.0.146/opencode-linux-arm64.tar.gz"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.linux-x86_64]
-archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-linux-x64.tar.gz"
+archive = "https://github.com/sst/opencode/releases/download/v1.0.146/opencode-linux-x64.tar.gz"
 cmd = "./opencode"
 args = ["acp"]
 
 [agent_servers.opencode.targets.windows-x86_64]
-archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-windows-x64.zip"
+archive = "https://github.com/sst/opencode/releases/download/v1.0.146/opencode-windows-x64.zip"
 cmd = "./opencode.exe"
 args = ["acp"]

+ 1 - 1
packages/function/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/function",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "$schema": "https://json.schemastore.org/package.json",
   "private": true,
   "type": "module",

+ 1 - 1
packages/opencode/package.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "name": "opencode",
   "type": "module",
   "private": true,

+ 8 - 9
packages/opencode/src/bus/index.ts

@@ -7,7 +7,13 @@ import { GlobalBus } from "./global"
 export namespace Bus {
   const log = Log.create({ service: "bus" })
   type Subscription = (event: any) => void
-  const disposedEventType = "server.instance.disposed"
+
+  export const InstanceDisposed = BusEvent.define(
+    "server.instance.disposed",
+    z.object({
+      directory: z.string(),
+    }),
+  )
 
   const state = Instance.state(
     () => {
@@ -21,7 +27,7 @@ export namespace Bus {
       const wildcard = entry.subscriptions.get("*")
       if (!wildcard) return
       const event = {
-        type: disposedEventType,
+        type: InstanceDisposed.type,
         properties: {
           directory: Instance.directory,
         },
@@ -32,13 +38,6 @@ export namespace Bus {
     },
   )
 
-  export const InstanceDisposed = BusEvent.define(
-    disposedEventType,
-    z.object({
-      directory: z.string(),
-    }),
-  )
-
   export async function publish<Definition extends BusEvent.Definition>(
     def: Definition,
     properties: z.output<Definition["properties"]>,

+ 1 - 1
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

@@ -894,7 +894,7 @@ export function Session() {
                                 <box marginTop={1}>
                                   <For each={revert()!.diffFiles}>
                                     {(file) => (
-                                      <text>
+                                      <text fg={theme.text}>
                                         {file.filename}
                                         <Show when={file.additions > 0}>
                                           <span style={{ fg: theme.diffAdded }}> +{file.additions}</span>

+ 10 - 0
packages/opencode/src/project/instance.ts

@@ -3,6 +3,7 @@ import { Context } from "../util/context"
 import { Project } from "./project"
 import { State } from "./state"
 import { iife } from "@/util/iife"
+import { GlobalBus } from "@/bus/global"
 
 interface Context {
   directory: string
@@ -52,6 +53,15 @@ export const Instance = {
     Log.Default.info("disposing instance", { directory: Instance.directory })
     await State.dispose(Instance.directory)
     cache.delete(Instance.directory)
+    GlobalBus.emit("event", {
+      directory: Instance.directory,
+      payload: {
+        type: "server.instance.disposed",
+        properties: {
+          directory: Instance.directory,
+        },
+      },
+    })
   },
   async disposeAll() {
     Log.Default.info("disposing all instances")

+ 10 - 0
packages/opencode/src/provider/provider.ts

@@ -318,6 +318,16 @@ export namespace Provider {
         },
       }
     },
+    cerebras: async () => {
+      return {
+        autoload: false,
+        options: {
+          headers: {
+            "X-Cerebras-3rd-Party-Integration": "opencode",
+          },
+        },
+      }
+    },
   }
 
   export const Model = z

+ 9 - 2
packages/opencode/src/provider/transform.ts

@@ -74,12 +74,17 @@ export namespace ProviderTransform {
       return result
     }
 
+    // TODO: rm later
+    const bugged =
+      (model.id === "kimi-k2-thinking" && model.providerID === "opencode") ||
+      (model.id === "moonshotai/Kimi-K2-Thinking" && model.providerID === "baseten")
     if (
       model.providerID === "deepseek" ||
       model.api.id.toLowerCase().includes("deepseek") ||
       (model.capabilities.interleaved &&
         typeof model.capabilities.interleaved === "object" &&
-        model.capabilities.interleaved.field === "reasoning_content")
+        model.capabilities.interleaved.field === "reasoning_content" &&
+        !bugged)
     ) {
       return msgs.map((msg) => {
         if (msg.role === "assistant" && Array.isArray(msg.content)) {
@@ -231,9 +236,11 @@ export namespace ProviderTransform {
 
     if (model.api.npm === "@ai-sdk/google" || model.api.npm === "@ai-sdk/google-vertex") {
       result["thinkingConfig"] = {
-        thinkingLevel: "high",
         includeThoughts: true,
       }
+      if (model.api.id.includes("gemini-3")) {
+        result["thinkingConfig"]["thinkingLevel"] = "high"
+      }
     }
 
     if (model.api.id.includes("gpt-5") && !model.api.id.includes("gpt-5-chat")) {

+ 30 - 0
packages/opencode/src/server/server.ts

@@ -56,6 +56,7 @@ export namespace Server {
 
   export const Event = {
     Connected: BusEvent.define("server.connected", z.object({})),
+    Disposed: BusEvent.define("global.disposed", z.object({})),
   }
 
   const app = new Hono()
@@ -140,6 +141,35 @@ export namespace Server {
           })
         },
       )
+      .post(
+        "/global/dispose",
+        describeRoute({
+          summary: "Dispose instance",
+          description: "Clean up and dispose all OpenCode instances, releasing all resources.",
+          operationId: "global.dispose",
+          responses: {
+            200: {
+              description: "Global disposed",
+              content: {
+                "application/json": {
+                  schema: resolver(z.boolean()),
+                },
+              },
+            },
+          },
+        }),
+        async (c) => {
+          await Instance.disposeAll()
+          GlobalBus.emit("event", {
+            directory: "global",
+            payload: {
+              type: Event.Disposed.type,
+              properties: {},
+            },
+          })
+          return c.json(true)
+        },
+      )
       .use(async (c, next) => {
         const directory = c.req.query("directory") ?? c.req.header("x-opencode-directory") ?? process.cwd()
         return Instance.provide({

+ 2 - 23
packages/opencode/src/session/index.ts

@@ -223,34 +223,13 @@ export namespace Session {
     if (cfg.share === "disabled") {
       throw new Error("Sharing is disabled in configuration")
     }
-
-    if (cfg.enterprise?.url) {
-      const { ShareNext } = await import("@/share/share-next")
-      const share = await ShareNext.create(id)
-      await update(id, (draft) => {
-        draft.share = {
-          url: share.url,
-        }
-      })
-    }
-
-    const session = await get(id)
-    if (session.share) return session.share
-    const { Share } = await import("../share/share")
-    const share = await Share.create(id)
+    const { ShareNext } = await import("@/share/share-next")
+    const share = await ShareNext.create(id)
     await update(id, (draft) => {
       draft.share = {
         url: share.url,
       }
     })
-    await Storage.write(["share", id], share)
-    await Share.sync("session/info/" + id, session)
-    for (const msg of await messages({ sessionID: id })) {
-      await Share.sync("session/message/" + id + "/" + msg.info.id, msg.info)
-      for (const part of msg.parts) {
-        await Share.sync("session/part/" + id + "/" + msg.info.id + "/" + part.id, part)
-      }
-    }
     return share
   })
 

+ 7 - 8
packages/opencode/src/share/share-next.ts

@@ -11,9 +11,11 @@ import type * as SDK from "@opencode-ai/sdk/v2"
 export namespace ShareNext {
   const log = Log.create({ service: "share-next" })
 
+  async function url() {
+    return Config.get().then((x) => x.enterprise?.url ?? "https://opncd.ai")
+  }
+
   export async function init() {
-    const config = await Config.get()
-    if (!config.enterprise) return
     Bus.subscribe(Session.Event.Updated, async (evt) => {
       await sync(evt.properties.info.id, [
         {
@@ -62,8 +64,7 @@ export namespace ShareNext {
 
   export async function create(sessionID: string) {
     log.info("creating share", { sessionID })
-    const url = await Config.get().then((x) => x.enterprise!.url)
-    const result = await fetch(`${url}/api/share`, {
+    const result = await fetch(`${await url()}/api/share`, {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
@@ -126,11 +127,10 @@ export namespace ShareNext {
       const queued = queue.get(sessionID)
       if (!queued) return
       queue.delete(sessionID)
-      const url = await Config.get().then((x) => x.enterprise!.url)
       const share = await get(sessionID)
       if (!share) return
 
-      await fetch(`${url}/api/share/${share.id}/sync`, {
+      await fetch(`${await url()}/api/share/${share.id}/sync`, {
         method: "POST",
         headers: {
           "Content-Type": "application/json",
@@ -146,10 +146,9 @@ export namespace ShareNext {
 
   export async function remove(sessionID: string) {
     log.info("removing share", { sessionID })
-    const url = await Config.get().then((x) => x.enterprise!.url)
     const share = await get(sessionID)
     if (!share) return
-    await fetch(`${url}/api/share/${share.id}`, {
+    await fetch(`${await url()}/api/share/${share.id}`, {
       method: "DELETE",
       headers: {
         "Content-Type": "application/json",

+ 1 - 2
packages/opencode/src/tool/bash.txt

@@ -20,8 +20,7 @@ Usage notes:
     - The command argument is required.
     - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes).
   If not specified, commands will timeout after 120000ms (2 minutes).
-    - It is very helpful if you write a clear, concise description of what this command
-  does in 5-10 words.
+    - The description argument is required. You must write a clear, concise description of what this command does in 5-10 words.
     - If the output exceeds 30000 characters, output will be truncated before being
   returned to you.
     - You can use the `run_in_background` parameter to run the command in the background,

+ 1 - 1
packages/plugin/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/plugin",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "scripts": {
     "typecheck": "tsgo --noEmit",

+ 1 - 1
packages/sdk/js/package.json

@@ -1,7 +1,7 @@
 {
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/sdk",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "scripts": {
     "typecheck": "tsgo --noEmit",

+ 13 - 0
packages/sdk/js/src/v2/gen/sdk.gen.ts

@@ -28,6 +28,7 @@ import type {
   FindSymbolsResponses,
   FindTextResponses,
   FormatterStatusResponses,
+  GlobalDisposeResponses,
   GlobalEventResponses,
   InstanceDisposeResponses,
   LspStatusResponses,
@@ -193,6 +194,18 @@ export class Global extends HeyApiClient {
       ...options,
     })
   }
+
+  /**
+   * Dispose instance
+   *
+   * Clean up and dispose all OpenCode instances, releasing all resources.
+   */
+  public dispose<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
+    return (options?.client ?? this.client).post<GlobalDisposeResponses, unknown, ThrowOnError>({
+      url: "/global/dispose",
+      ...options,
+    })
+  }
 }
 
 export class Project extends HeyApiClient {

+ 24 - 0
packages/sdk/js/src/v2/gen/types.gen.ts

@@ -725,6 +725,13 @@ export type EventServerConnected = {
   }
 }
 
+export type EventGlobalDisposed = {
+  type: "global.disposed"
+  properties: {
+    [key: string]: unknown
+  }
+}
+
 export type Event =
   | EventInstallationUpdated
   | EventInstallationUpdateAvailable
@@ -759,6 +766,7 @@ export type Event =
   | EventPtyExited
   | EventPtyDeleted
   | EventServerConnected
+  | EventGlobalDisposed
 
 export type GlobalEvent = {
   directory: string
@@ -1700,6 +1708,22 @@ export type GlobalEventResponses = {
 
 export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
 
+export type GlobalDisposeData = {
+  body?: never
+  path?: never
+  query?: never
+  url: "/global/dispose"
+}
+
+export type GlobalDisposeResponses = {
+  /**
+   * Global disposed
+   */
+  200: boolean
+}
+
+export type GlobalDisposeResponse = GlobalDisposeResponses[keyof GlobalDisposeResponses]
+
 export type ProjectListData = {
   body?: never
   path?: never

+ 42 - 0
packages/sdk/openapi.json

@@ -31,6 +31,31 @@
         ]
       }
     },
+    "/global/dispose": {
+      "post": {
+        "operationId": "global.dispose",
+        "summary": "Dispose instance",
+        "description": "Clean up and dispose all OpenCode instances, releasing all resources.",
+        "responses": {
+          "200": {
+            "description": "Global disposed",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "boolean"
+                }
+              }
+            }
+          }
+        },
+        "x-codeSamples": [
+          {
+            "lang": "js",
+            "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.global.dispose({\n  ...\n})"
+          }
+        ]
+      }
+    },
     "/project": {
       "get": {
         "operationId": "project.list",
@@ -6810,6 +6835,20 @@
         },
         "required": ["type", "properties"]
       },
+      "Event.global.disposed": {
+        "type": "object",
+        "properties": {
+          "type": {
+            "type": "string",
+            "const": "global.disposed"
+          },
+          "properties": {
+            "type": "object",
+            "properties": {}
+          }
+        },
+        "required": ["type", "properties"]
+      },
       "Event": {
         "anyOf": [
           {
@@ -6910,6 +6949,9 @@
           },
           {
             "$ref": "#/components/schemas/Event.server.connected"
+          },
+          {
+            "$ref": "#/components/schemas/Event.global.disposed"
           }
         ]
       },

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/slack",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "scripts": {
     "dev": "bun run src/index.ts",

+ 1 - 1
packages/tauri/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@opencode-ai/tauri",
   "private": true,
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "scripts": {
     "typecheck": "tsgo -b",

BIN
packages/tauri/src-tauri/icons/128x128.png


BIN
packages/tauri/src-tauri/icons/[email protected]


BIN
packages/tauri/src-tauri/icons/32x32.png


BIN
packages/tauri/src-tauri/icons/64x64.png


BIN
packages/tauri/src-tauri/icons/Square107x107Logo.png


BIN
packages/tauri/src-tauri/icons/Square142x142Logo.png


BIN
packages/tauri/src-tauri/icons/Square150x150Logo.png


BIN
packages/tauri/src-tauri/icons/Square284x284Logo.png


BIN
packages/tauri/src-tauri/icons/Square30x30Logo.png


BIN
packages/tauri/src-tauri/icons/Square310x310Logo.png


BIN
packages/tauri/src-tauri/icons/Square44x44Logo.png


BIN
packages/tauri/src-tauri/icons/Square71x71Logo.png


BIN
packages/tauri/src-tauri/icons/Square89x89Logo.png


BIN
packages/tauri/src-tauri/icons/StoreLogo.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png


BIN
packages/tauri/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png


BIN
packages/tauri/src-tauri/icons/icon.icns


BIN
packages/tauri/src-tauri/icons/icon.ico


BIN
packages/tauri/src-tauri/icons/icon.png


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


BIN
packages/tauri/src-tauri/icons/ios/[email protected]


+ 14 - 2
packages/tauri/tsconfig.json

@@ -1,7 +1,19 @@
 {
-  "extends": "../desktop/tsconfig.json",
   "compilerOptions": {
-    "outDir": "ts-dist"
+    "target": "ESNext",
+    "module": "ESNext",
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "jsx": "preserve",
+    "jsxImportSource": "solid-js",
+    "allowJs": true,
+    "strict": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "emitDeclarationOnly": false,
+    "outDir": "node_modules/.ts-dist"
   },
   "references": [{ "path": "../desktop" }],
   "include": ["src"]

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/ui",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "type": "module",
   "exports": {
     "./*": "./src/components/*.tsx",

+ 1 - 1
packages/util/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@opencode-ai/util",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "private": true,
   "type": "module",
   "exports": {

+ 1 - 1
packages/web/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@opencode-ai/web",
   "type": "module",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "scripts": {
     "dev": "astro dev",
     "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",

+ 14 - 14
packages/web/src/content/docs/sdk.mdx

@@ -37,13 +37,13 @@ This starts both a server and a client
 
 #### Options
 
-| Option          | Type       | Description                      | Default                 |
-| --------------- | ---------- | -------------------------------- | ----------------------- |
-| `baseUrl`       | `string`   | URL of the server                | `http://localhost:4096` |
-| `fetch`         | `function` | Custom fetch implementation      | `globalThis.fetch`      |
-| `parseAs`       | `string`   | Response parsing method          | `auto`                  |
-| `responseStyle` | `string`   | Return style: `data` or `fields` | `fields`                |
-| `throwOnError`  | `boolean`  | Throw errors instead of return   | `false`                 |
+| Option     | Type          | Description                    | Default     |
+| ---------- | ------------- | ------------------------------ | ----------- |
+| `hostname` | `string`      | Server hostname                | `127.0.0.1` |
+| `port`     | `number`      | Server port                    | `4096`      |
+| `signal`   | `AbortSignal` | Abort signal for cancellation  | `undefined` |
+| `timeout`  | `number`      | Timeout in ms for server start | `5000`      |
+| `config`   | `Config`      | Configuration object           | `{}`        |
 
 ---
 
@@ -81,13 +81,13 @@ const client = createOpencodeClient({
 
 #### Options
 
-| Option     | Type          | Description                    | Default     |
-| ---------- | ------------- | ------------------------------ | ----------- |
-| `hostname` | `string`      | Server hostname                | `127.0.0.1` |
-| `port`     | `number`      | Server port                    | `4096`      |
-| `signal`   | `AbortSignal` | Abort signal for cancellation  | `undefined` |
-| `timeout`  | `number`      | Timeout in ms for server start | `5000`      |
-| `config`   | `Config`      | Configuration object           | `{}`        |
+| Option          | Type       | Description                      | Default                 |
+| --------------- | ---------- | -------------------------------- | ----------------------- |
+| `baseUrl`       | `string`   | URL of the server                | `http://localhost:4096` |
+| `fetch`         | `function` | Custom fetch implementation      | `globalThis.fetch`      |
+| `parseAs`       | `string`   | Response parsing method          | `auto`                  |
+| `responseStyle` | `string`   | Return style: `data` or `fields` | `fields`                |
+| `throwOnError`  | `boolean`  | Throw errors instead of return   | `false`                 |
 
 ---
 

+ 1 - 1
sdks/vscode/package.json

@@ -2,7 +2,7 @@
   "name": "opencode",
   "displayName": "opencode",
   "description": "opencode for VS Code",
-  "version": "1.0.143",
+  "version": "1.0.146",
   "publisher": "sst-dev",
   "repository": {
     "type": "git",