Ver código fonte

Merge branch 'dev' into refactor/node-server-adapter

Dax 5 meses atrás
pai
commit
0ea7e75e0b
43 arquivos alterados com 597 adições e 104 exclusões
  1. 7 13
      .opencode/tool/github-triage.ts
  2. 0 2
      .opencode/tool/github-triage.txt
  3. 10 10
      bun.lock
  4. 4 4
      nix/hashes.json
  5. 17 8
      packages/app/src/components/debug-bar.tsx
  6. 2 2
      packages/opencode/package.json
  7. 3 0
      packages/opencode/src/cli/cmd/tui/app.tsx
  8. 1 1
      packages/opencode/src/cli/cmd/tui/component/dialog-workspace-list.tsx
  9. 4 1
      packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
  10. 1 0
      packages/opencode/src/cli/cmd/tui/context/route.tsx
  11. 1 0
      packages/opencode/src/cli/cmd/tui/routes/home.tsx
  12. 8 0
      packages/opencode/src/config/config.ts
  13. 8 2
      packages/opencode/src/mcp/index.ts
  14. 12 3
      packages/opencode/src/mcp/oauth-provider.ts
  15. 37 26
      packages/opencode/src/project/project.ts
  16. 64 9
      packages/opencode/src/provider/provider.ts
  17. 33 2
      packages/opencode/src/provider/transform.ts
  18. 5 1
      packages/opencode/src/session/index.ts
  19. 1 1
      packages/opencode/src/worktree/index.ts
  20. 199 0
      packages/opencode/test/mcp/oauth-auto-connect.test.ts
  21. 2 0
      packages/opencode/test/provider/provider.test.ts
  22. 140 0
      packages/opencode/test/provider/transform.test.ts
  23. 2 0
      packages/sdk/js/src/v2/gen/sdk.gen.ts
  24. 6 1
      packages/sdk/js/src/v2/gen/types.gen.ts
  25. 10 0
      packages/sdk/openapi.json
  26. 1 1
      packages/web/src/content/docs/ar/ecosystem.mdx
  27. 1 1
      packages/web/src/content/docs/bs/ecosystem.mdx
  28. 3 1
      packages/web/src/content/docs/config.mdx
  29. 1 1
      packages/web/src/content/docs/da/ecosystem.mdx
  30. 1 1
      packages/web/src/content/docs/de/ecosystem.mdx
  31. 1 1
      packages/web/src/content/docs/es/ecosystem.mdx
  32. 1 1
      packages/web/src/content/docs/fr/ecosystem.mdx
  33. 1 1
      packages/web/src/content/docs/it/ecosystem.mdx
  34. 1 1
      packages/web/src/content/docs/ja/ecosystem.mdx
  35. 1 1
      packages/web/src/content/docs/ko/ecosystem.mdx
  36. 1 1
      packages/web/src/content/docs/nb/ecosystem.mdx
  37. 1 1
      packages/web/src/content/docs/pl/ecosystem.mdx
  38. 1 1
      packages/web/src/content/docs/pt-br/ecosystem.mdx
  39. 1 1
      packages/web/src/content/docs/ru/ecosystem.mdx
  40. 1 1
      packages/web/src/content/docs/th/ecosystem.mdx
  41. 1 1
      packages/web/src/content/docs/tr/ecosystem.mdx
  42. 1 1
      packages/web/src/content/docs/zh-cn/ecosystem.mdx
  43. 1 1
      packages/web/src/content/docs/zh-tw/ecosystem.mdx

+ 7 - 13
.opencode/tool/github-triage.ts

@@ -5,16 +5,8 @@ import DESCRIPTION from "./github-triage.txt"
 const TEAM = {
   desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
   zen: ["fwang", "MrMushrooooom"],
-  tui: [
-    "thdxr",
-    "kommander",
-    // "rekram1-node" (on vacation)
-  ],
-  core: [
-    "thdxr",
-    // "rekram1-node", (on vacation)
-    "jlongster",
-  ],
+  tui: ["thdxr", "kommander", "rekram1-node"],
+  core: ["thdxr", "rekram1-node", "jlongster"],
   docs: ["R44VC0RP"],
   windows: ["Hona"],
 } as const
@@ -50,7 +42,10 @@ async function githubFetch(endpoint: string, options: RequestInit = {}) {
 export default tool({
   description: DESCRIPTION,
   args: {
-    assignee: tool.schema.enum(ASSIGNEES as [string, ...string[]]).describe("The username of the assignee"),
+    assignee: tool.schema
+      .enum(ASSIGNEES as [string, ...string[]])
+      .describe("The username of the assignee")
+      .default("rekram1-node"),
     labels: tool.schema
       .array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"]))
       .describe("The labels(s) to add to the issue")
@@ -73,8 +68,7 @@ export default tool({
       results.push("Dropped label: nix (issue does not mention nix)")
     }
 
-    // const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
-    const assignee = web ? pick(TEAM.desktop) : args.assignee
+    const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
 
     if (labels.includes("zen") && !zen) {
       throw new Error("Only add the zen label when issue title/body contains 'zen'")

+ 0 - 2
.opencode/tool/github-triage.txt

@@ -4,5 +4,3 @@ Choose labels and assignee using the current triage policy and ownership rules.
 Pick the most fitting labels for the issue and assign one owner.
 
 If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.
-
-(Note: rekram1-node is on vacation, do not assign issues to him.)

+ 10 - 10
bun.lock

@@ -335,8 +335,8 @@
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/util": "workspace:*",
         "@openrouter/ai-sdk-provider": "1.5.4",
-        "@opentui/core": "0.1.86",
-        "@opentui/solid": "0.1.86",
+        "@opentui/core": "0.1.87",
+        "@opentui/solid": "0.1.87",
         "@parcel/watcher": "2.5.1",
         "@pierre/diffs": "catalog:",
         "@solid-primitives/event-bus": "1.1.2",
@@ -1422,21 +1422,21 @@
 
     "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
 
-    "@opentui/core": ["@opentui/core@0.1.86", "", { "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.86", "@opentui/core-darwin-x64": "0.1.86", "@opentui/core-linux-arm64": "0.1.86", "@opentui/core-linux-x64": "0.1.86", "@opentui/core-win32-arm64": "0.1.86", "@opentui/core-win32-x64": "0.1.86", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-3tRLbI9ADrQE1jEEn4x2aJexEOQZkv9Emk2BixMZqxfVhz2zr2SxtpimDAX0vmZK3+GnWAwBWxuaCAsxZpY4+w=="],
+    "@opentui/core": ["@opentui/core@0.1.87", "", { "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.87", "@opentui/core-darwin-x64": "0.1.87", "@opentui/core-linux-arm64": "0.1.87", "@opentui/core-linux-x64": "0.1.87", "@opentui/core-win32-arm64": "0.1.87", "@opentui/core-win32-x64": "0.1.87", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-dhsmMv0IqKftwG7J/pBrLBj2armsYIg5R3LBvciRQI/6X89GufP4l1u0+QTACAx6iR4SYJJNVNQ2tdX8LM9rMw=="],
 
-    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.86", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Zp7q64+d+Dcx6YrH3mRcnHq8EOBnrfc1RvjgSWLhpXr49hY6LzuhqpfZM57aGErPYlR+ff8QM6e5FUkFnDfyjw=="],
+    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.87", "", { "os": "darwin", "cpu": "arm64" }, "sha512-G8oq85diOfkU6n0T1CxCle7oDmpKxwhcdhZ9khBMU5IrfLx9ZDuCM3F6MsiRQWdvPPCq2oomNbd64bYkPamYgw=="],
 
-    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.86", "", { "os": "darwin", "cpu": "x64" }, "sha512-NcxfjCJm1kLnTMVOpAPdRYNi8W8XdAXNa6N7i9khiVFrl2v5KRQfUjbrSOUYVxFJNc3jKFG6rsn3jEApvn92qA=="],
+    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.87", "", { "os": "darwin", "cpu": "x64" }, "sha512-MYTFQfOHm6qO7YaY4GHK9u/oJlXY6djaaxl5I+k4p2mk3vvuFIl/AP1ypITwBFjyV5gyp7PRWFp4nGfY9oN8bw=="],
 
-    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.86", "", { "os": "linux", "cpu": "arm64" }, "sha512-EDHAvqSOr8CXzbDvo1aE5blJ6wu1aSbR2LqoXtoeXHemr2T2W42D2TdIWewG6K+/BuRbzZnqt9wnYFBksLW6lw=="],
+    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.87", "", { "os": "linux", "cpu": "arm64" }, "sha512-he8o1h5M6oskRJ7wE+xKJgmWnv5ZwN6gB3M/Z+SeHtOMPa5cZmi3TefTjG54llEgFfx0F9RcqHof7TJ/GNxRkw=="],
 
-    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.86", "", { "os": "linux", "cpu": "x64" }, "sha512-VBaBkVdQDxYV4WcKjb+jgyMS5PiVHepvfaoKWpz1Bq+J01xXW4XPcXyPGkgR1+2R93KzaugEnLscTW4mWtLHlQ=="],
+    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.87", "", { "os": "linux", "cpu": "x64" }, "sha512-aiUwjPlH4yDcB8/6YDKSmMkaoGAAltL0Xo0AzXyAtJXWK5tkCSaYjEVwzJ/rYRkr4Magnad+Mjth4AQUWdR2AA=="],
 
-    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.86", "", { "os": "win32", "cpu": "arm64" }, "sha512-xKbT7sEKYKGwUPkoqmLfHjbJU+vwHPDwf/r/mIunL41JXQBB35CSZ3/QgIwpp2kkteu7oE1tdBdg15ogUU4OMg=="],
+    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.87", "", { "os": "win32", "cpu": "arm64" }, "sha512-cmP0pOyREjWGniHqbDmaMY7U+1AyagrD8VseJbU0cGpNgVpG2/gbrJUGdfdLB0SNb+mzLdx6SOjdxtrElwRCQA=="],
 
-    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.86", "", { "os": "win32", "cpu": "x64" }, "sha512-HRfgAUlcu71/MrtgfX4Gj7PsDtfXZiuC506Pkn1OnRN1Xomcu10BVRDweUa0/g8ldU9i9kLjMGGnpw6/NjaBFg=="],
+    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.87", "", { "os": "win32", "cpu": "x64" }, "sha512-N2GErAAP8iODf2RPp86pilPaVKiD6G4pkpZL5nLGbKsl0bndrVTpSqZcn8+/nQwFZDPD/AsiRTYNOfWOblhzOw=="],
 
-    "@opentui/solid": ["@opentui/solid@0.1.86", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.86", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-pOZC9dlZIH+bpstVVZ2AvYukBnslZTKSl/y5H8FWcMTHGv/BzpGxXBxstL65E/IQASqPFbvFcs7yMRzdLhynmA=="],
+    "@opentui/solid": ["@opentui/solid@0.1.87", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.87", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-lRT9t30l8+FtgOjjWJcdb2MT6hP8/RKqwGgYwTI7fXrOqdhxxwdP2SM+rH2l3suHeASheiTdlvPAo230iUcsvg=="],
 
     "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
 

+ 4 - 4
nix/hashes.json

@@ -1,8 +1,8 @@
 {
   "nodeModules": {
-    "x86_64-linux": "sha256-+SMpaj0jeIHjlddAu6QIwojmWFVIiA8/G32hiQMjcOk=",
-    "aarch64-linux": "sha256-uo63IF6OCMab+O3ngn1sVxqIGJMm04HXuDgIRmXNTNk=",
-    "aarch64-darwin": "sha256-yB2tWm6AsX6UifnDqe7VldhN5zTQkDoqZ87AGQYjxT4=",
-    "x86_64-darwin": "sha256-nNhtqMSG4/y+uxjj14Jc5QQ7X6hQli9ni4v56XAvaAU="
+    "x86_64-linux": "sha256-duBedS4ZTc1as03OM0KB9mKKU21Cywv4o9GHwQZv6Ts=",
+    "aarch64-linux": "sha256-juvQfuNBqqzeB/TIY9PuUDqgpsdyI54ImowjQLrNhns=",
+    "aarch64-darwin": "sha256-kKgcuEN1oJqHJc+sGjcZ4INWvbZczSTDJ8VHIWAquD4=",
+    "x86_64-darwin": "sha256-hXkFWOL4wi9s8HSrChpqtH4PKSNzbzVgU+0GbAxEUT4="
   }
 }

+ 17 - 8
packages/app/src/components/debug-bar.tsx

@@ -49,14 +49,19 @@ const bad = (n: number | undefined, limit: number, low = false) => {
 
 const session = (path: string) => path.includes("/session")
 
-function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string; value: string }) {
+function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string; value: string; wide?: boolean }) {
   return (
-    <Tooltip value={props.tip} placement="left">
-      <div class="flex w-full flex-col items-center px-0.5 py-1 text-center">
-        <div class="text-[7px] font-black uppercase tracking-[0.04em] opacity-70 leading-none">{props.label}</div>
+    <Tooltip value={props.tip} placement="top">
+      <div
+        classList={{
+          "flex min-h-[42px] w-full min-w-0 flex-col items-center justify-center rounded-[8px] bg-white/5 px-0.5 py-1 text-center": true,
+          "col-span-2": !!props.wide,
+        }}
+      >
+        <div class="text-[10px] leading-none font-black uppercase tracking-[0.04em] opacity-70">{props.label}</div>
         <div
           classList={{
-            "text-[9px] font-semibold leading-none tabular-nums": true,
+            "text-[13px] leading-none font-bold tabular-nums sm:text-[14px]": true,
             "text-text-on-critical-base": !!props.bad,
             "opacity-70": !!props.dim,
           }}
@@ -355,10 +360,13 @@ export function DebugBar() {
   return (
     <aside
       aria-label="Development performance diagnostics"
-      class="pointer-events-auto h-full min-h-0 w-[36px] shrink-0 overflow-y-auto text-text-on-interactive-base no-scrollbar sm:w-[38px]"
-      style={{ "background-color": "color-mix(in srgb, var(--icon-interactive-base) 42%, black)" }}
+      class="pointer-events-auto fixed bottom-3 right-3 z-50 w-[308px] max-w-[calc(100vw-1.5rem)] overflow-hidden rounded-xl border p-0.5 text-text-on-interactive-base shadow-[var(--shadow-lg-border-base)] sm:bottom-4 sm:right-4 sm:w-[324px]"
+      style={{
+        "background-color": "color-mix(in srgb, var(--icon-interactive-base) 42%, black)",
+        "border-color": "color-mix(in srgb, white 14%, transparent)",
+      }}
     >
-      <div class="flex min-h-full flex-col gap-0.5 py-2 font-mono">
+      <div class="grid grid-cols-5 gap-px font-mono">
         <Cell
           label="NAV"
           tip="Last completed route transition touching a session page, measured from router start until the first paint after it settles."
@@ -425,6 +433,7 @@ export function DebugBar() {
           value={heapv()}
           bad={bad(heap(), 0.8)}
           dim={state.heap.used === undefined}
+          wide
         />
       </div>
     </aside>

+ 2 - 2
packages/opencode/package.json

@@ -93,8 +93,8 @@
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@openrouter/ai-sdk-provider": "1.5.4",
-    "@opentui/core": "0.1.86",
-    "@opentui/solid": "0.1.86",
+    "@opentui/core": "0.1.87",
+    "@opentui/solid": "0.1.87",
     "@parcel/watcher": "2.5.1",
     "@pierre/diffs": "catalog:",
     "@solid-primitives/event-bus": "1.1.2",

+ 3 - 0
packages/opencode/src/cli/cmd/tui/app.tsx

@@ -402,9 +402,12 @@ function App() {
         const current = promptRef.current
         // Don't require focus - if there's any text, preserve it
         const currentPrompt = current?.current?.input ? current.current : undefined
+        const workspaceID =
+          route.data.type === "session" ? sync.session.get(route.data.sessionID)?.workspaceID : undefined
         route.navigate({
           type: "home",
           initialPrompt: currentPrompt,
+          workspaceID,
         })
         dialog.clear()
       },

+ 1 - 1
packages/opencode/src/cli/cmd/tui/component/dialog-workspace-list.tsx

@@ -47,7 +47,7 @@ async function openWorkspace(input: {
   }
   let created: Session | undefined
   while (!created) {
-    const result = await client.session.create({}).catch(() => undefined)
+    const result = await client.session.create({ workspaceID: input.workspaceID }).catch(() => undefined)
     if (!result) {
       input.toast.show({
         message: "Failed to open workspace",

+ 4 - 1
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -37,6 +37,7 @@ import { DialogSkill } from "../dialog-skill"
 
 export type PromptProps = {
   sessionID?: string
+  workspaceID?: string
   visible?: boolean
   disabled?: boolean
   onSubmit?: () => void
@@ -542,7 +543,9 @@ export function Prompt(props: PromptProps) {
 
     let sessionID = props.sessionID
     if (sessionID == null) {
-      const res = await sdk.client.session.create({})
+      const res = await sdk.client.session.create({
+        workspaceID: props.workspaceID,
+      })
 
       if (res.error) {
         console.log("Creating a session failed:", res.error)

+ 1 - 0
packages/opencode/src/cli/cmd/tui/context/route.tsx

@@ -5,6 +5,7 @@ import type { PromptInfo } from "../component/prompt/history"
 export type HomeRoute = {
   type: "home"
   initialPrompt?: PromptInfo
+  workspaceID?: string
 }
 
 export type SessionRoute = {

+ 1 - 0
packages/opencode/src/cli/cmd/tui/routes/home.tsx

@@ -121,6 +121,7 @@ export function Home() {
               promptRef.set(r)
             }}
             hint={Hint}
+            workspaceID={route.workspaceID}
           />
         </box>
         <box height={4} minHeight={0} width="100%" maxWidth={75} alignItems="center" paddingTop={3} flexShrink={1}>

+ 8 - 0
packages/opencode/src/config/config.ts

@@ -972,6 +972,14 @@ export namespace Config {
             .describe(
               "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
             ),
+          chunkTimeout: z
+            .number()
+            .int()
+            .positive()
+            .optional()
+            .describe(
+              "Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
+            ),
         })
         .catchall(z.any())
         .optional(),

+ 8 - 2
packages/opencode/src/mcp/index.ts

@@ -396,8 +396,14 @@ export namespace MCP {
         } catch (error) {
           lastError = error instanceof Error ? error : new Error(String(error))
 
-          // Handle OAuth-specific errors
-          if (error instanceof UnauthorizedError) {
+          // Handle OAuth-specific errors.
+          // The SDK throws UnauthorizedError when auth() returns 'REDIRECT',
+          // but may also throw plain Errors when auth() fails internally
+          // (e.g. during discovery, registration, or state generation).
+          // When an authProvider is attached, treat both cases as auth-related.
+          const isAuthError =
+            error instanceof UnauthorizedError || (authProvider && lastError.message.includes("OAuth"))
+          if (isAuthError) {
             log.info("mcp server requires authentication", { key, transport: name })
 
             // Check if this is a "needs registration" error

+ 12 - 3
packages/opencode/src/mcp/oauth-provider.ts

@@ -144,10 +144,19 @@ export class McpOAuthProvider implements OAuthClientProvider {
 
   async state(): Promise<string> {
     const entry = await McpAuth.get(this.mcpName)
-    if (!entry?.oauthState) {
-      throw new Error(`No OAuth state saved for MCP server: ${this.mcpName}`)
+    if (entry?.oauthState) {
+      return entry.oauthState
     }
-    return entry.oauthState
+
+    // Generate a new state if none exists — the SDK calls state() as a
+    // generator, not just a reader, so we need to produce a value even when
+    // startAuth() hasn't pre-saved one (e.g. during automatic auth on first
+    // connect).
+    const newState = Array.from(crypto.getRandomValues(new Uint8Array(32)))
+      .map((b) => b.toString(16).padStart(2, "0"))
+      .join("")
+    await McpAuth.updateOAuthState(this.mcpName, newState)
+    return newState
   }
 
   async invalidateCredentials(type: "all" | "client" | "tokens"): Promise<void> {

+ 37 - 26
packages/opencode/src/project/project.ts

@@ -88,6 +88,12 @@ export namespace Project {
     }
   }
 
+  function readCachedId(dir: string) {
+    return Filesystem.readText(path.join(dir, "opencode"))
+      .then((x) => x.trim())
+      .catch(() => undefined)
+  }
+
   export async function fromDirectory(directory: string) {
     log.info("fromDirectory", { directory })
 
@@ -101,19 +107,43 @@ export namespace Project {
         const gitBinary = which("git")
 
         // cached id calculation
-        let id = await Filesystem.readText(path.join(dotgit, "opencode"))
-          .then((x) => x.trim())
-          .catch(() => undefined)
+        let id = await readCachedId(dotgit)
 
         if (!gitBinary) {
           return {
             id: id ?? "global",
             worktree: sandbox,
-            sandbox: sandbox,
+            sandbox,
+            vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
+          }
+        }
+
+        const worktree = await git(["rev-parse", "--git-common-dir"], {
+          cwd: sandbox,
+        })
+          .then(async (result) => {
+            const common = gitpath(sandbox, await result.text())
+            // Avoid going to parent of sandbox when git-common-dir is empty.
+            return common === sandbox ? sandbox : path.dirname(common)
+          })
+          .catch(() => undefined)
+
+        if (!worktree) {
+          return {
+            id: id ?? "global",
+            worktree: sandbox,
+            sandbox,
             vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
           }
         }
 
+        // In the case of a git worktree, it can't cache the id
+        // because `.git` is not a folder, but it always needs the
+        // same project id as the common dir, so we resolve it now
+        if (id == null) {
+          id = await readCachedId(path.join(worktree, ".git"))
+        }
+
         // generate id from root commit
         if (!id) {
           const roots = await git(["rev-list", "--max-parents=0", "--all"], {
@@ -132,7 +162,7 @@ export namespace Project {
             return {
               id: "global",
               worktree: sandbox,
-              sandbox: sandbox,
+              sandbox,
               vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
             }
           }
@@ -147,7 +177,7 @@ export namespace Project {
           return {
             id: "global",
             worktree: sandbox,
-            sandbox: sandbox,
+            sandbox,
             vcs: "git",
           }
         }
@@ -161,33 +191,14 @@ export namespace Project {
         if (!top) {
           return {
             id,
-            sandbox,
             worktree: sandbox,
+            sandbox,
             vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
           }
         }
 
         sandbox = top
 
-        const worktree = await git(["rev-parse", "--git-common-dir"], {
-          cwd: sandbox,
-        })
-          .then(async (result) => {
-            const common = gitpath(sandbox, await result.text())
-            // Avoid going to parent of sandbox when git-common-dir is empty.
-            return common === sandbox ? sandbox : path.dirname(common)
-          })
-          .catch(() => undefined)
-
-        if (!worktree) {
-          return {
-            id,
-            sandbox,
-            worktree: sandbox,
-            vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
-          }
-        }
-
         return {
           id,
           sandbox,

+ 64 - 9
packages/opencode/src/provider/provider.ts

@@ -46,6 +46,8 @@ import { GoogleAuth } from "google-auth-library"
 import { ProviderTransform } from "./transform"
 import { Installation } from "../installation"
 
+const DEFAULT_CHUNK_TIMEOUT = 120_000
+
 export namespace Provider {
   const log = Log.create({ service: "provider" })
 
@@ -85,6 +87,54 @@ export namespace Provider {
     })
   }
 
+  function wrapSSE(res: Response, ms: number, ctl: AbortController) {
+    if (typeof ms !== "number" || ms <= 0) return res
+    if (!res.body) return res
+    if (!res.headers.get("content-type")?.includes("text/event-stream")) return res
+
+    const reader = res.body.getReader()
+    const body = new ReadableStream<Uint8Array>({
+      async pull(ctrl) {
+        const part = await new Promise<Awaited<ReturnType<typeof reader.read>>>((resolve, reject) => {
+          const id = setTimeout(() => {
+            const err = new Error("SSE read timed out")
+            ctl.abort(err)
+            void reader.cancel(err)
+            reject(err)
+          }, ms)
+
+          reader.read().then(
+            (part) => {
+              clearTimeout(id)
+              resolve(part)
+            },
+            (err) => {
+              clearTimeout(id)
+              reject(err)
+            },
+          )
+        })
+
+        if (part.done) {
+          ctrl.close()
+          return
+        }
+
+        ctrl.enqueue(part.value)
+      },
+      async cancel(reason) {
+        ctl.abort(reason)
+        await reader.cancel(reason)
+      },
+    })
+
+    return new Response(body, {
+      headers: new Headers(res.headers),
+      status: res.status,
+      statusText: res.statusText,
+    })
+  }
+
   const BUNDLED_PROVIDERS: Record<string, (options: any) => SDK> = {
     "@ai-sdk/amazon-bedrock": createAmazonBedrock,
     "@ai-sdk/anthropic": createAnthropic,
@@ -1092,21 +1142,23 @@ export namespace Provider {
       if (existing) return existing
 
       const customFetch = options["fetch"]
+      const chunkTimeout = options["chunkTimeout"] || DEFAULT_CHUNK_TIMEOUT
+      delete options["chunkTimeout"]
 
       options["fetch"] = async (input: any, init?: BunFetchRequestInit) => {
         // Preserve custom fetch if it exists, wrap it with timeout logic
         const fetchFn = customFetch ?? fetch
         const opts = init ?? {}
+        const chunkAbortCtl = typeof chunkTimeout === "number" && chunkTimeout > 0 ? new AbortController() : undefined
+        const signals: AbortSignal[] = []
 
-        if (options["timeout"] !== undefined && options["timeout"] !== null) {
-          const signals: AbortSignal[] = []
-          if (opts.signal) signals.push(opts.signal)
-          if (options["timeout"] !== false) signals.push(AbortSignal.timeout(options["timeout"]))
-
-          const combined = signals.length > 1 ? AbortSignal.any(signals) : signals[0]
+        if (opts.signal) signals.push(opts.signal)
+        if (chunkAbortCtl) signals.push(chunkAbortCtl.signal)
+        if (options["timeout"] !== undefined && options["timeout"] !== null && options["timeout"] !== false)
+          signals.push(AbortSignal.timeout(options["timeout"]))
 
-          opts.signal = combined
-        }
+        const combined = signals.length === 0 ? null : signals.length === 1 ? signals[0] : AbortSignal.any(signals)
+        if (combined) opts.signal = combined
 
         // Strip openai itemId metadata following what codex does
         // Codex uses #[serde(skip_serializing)] on id fields for all item types:
@@ -1126,11 +1178,14 @@ export namespace Provider {
           }
         }
 
-        return fetchFn(input, {
+        const res = await fetchFn(input, {
           ...opts,
           // @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682
           timeout: false,
         })
+
+        if (!chunkAbortCtl) return res
+        return wrapSSE(res, chunkTimeout, chunkAbortCtl)
       }
 
       const bundledFn = BUNDLED_PROVIDERS[model.api.npm]

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

@@ -657,9 +657,21 @@ export namespace ProviderTransform {
         // https://v5.ai-sdk.dev/providers/ai-sdk-providers/perplexity
         return {}
 
-      case "@mymediset/sap-ai-provider":
       case "@jerome-benoit/sap-ai-provider-v2":
         if (model.api.id.includes("anthropic")) {
+          if (isAnthropicAdaptive) {
+            return Object.fromEntries(
+              adaptiveEfforts.map((effort) => [
+                effort,
+                {
+                  thinking: {
+                    type: "adaptive",
+                  },
+                  effort,
+                },
+              ]),
+            )
+          }
           return {
             high: {
               thinking: {
@@ -675,7 +687,26 @@ export namespace ProviderTransform {
             },
           }
         }
-        return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
+        if (model.api.id.includes("gemini") && id.includes("2.5")) {
+          return {
+            high: {
+              thinkingConfig: {
+                includeThoughts: true,
+                thinkingBudget: 16000,
+              },
+            },
+            max: {
+              thinkingConfig: {
+                includeThoughts: true,
+                thinkingBudget: 24576,
+              },
+            },
+          }
+        }
+        if (model.api.id.includes("gpt") || /\bo[1-9]/.test(model.api.id)) {
+          return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
+        }
+        return {}
     }
     return {}
   }

+ 5 - 1
packages/opencode/src/session/index.ts

@@ -219,6 +219,7 @@ export namespace Session {
         parentID: Identifier.schema("session").optional(),
         title: z.string().optional(),
         permission: Info.shape.permission,
+        workspaceID: Identifier.schema("workspace").optional(),
       })
       .optional(),
     async (input) => {
@@ -227,6 +228,7 @@ export namespace Session {
         directory: Instance.directory,
         title: input?.title,
         permission: input?.permission,
+        workspaceID: input?.workspaceID,
       })
     },
   )
@@ -242,6 +244,7 @@ export namespace Session {
       const title = getForkedTitle(original.title)
       const session = await createNext({
         directory: Instance.directory,
+        workspaceID: original.workspaceID,
         title,
       })
       const msgs = await messages({ sessionID: input.sessionID })
@@ -292,6 +295,7 @@ export namespace Session {
     id?: string
     title?: string
     parentID?: string
+    workspaceID?: string
     directory: string
     permission?: PermissionNext.Ruleset
   }) {
@@ -301,7 +305,7 @@ export namespace Session {
       version: Installation.VERSION,
       projectID: Instance.project.id,
       directory: input.directory,
-      workspaceID: WorkspaceContext.workspaceID,
+      workspaceID: input.workspaceID,
       parentID: input.parentID,
       title: input.title ?? createDefaultTitle(!!input.parentID),
       permission: input.permission,

+ 1 - 1
packages/opencode/src/worktree/index.ts

@@ -413,7 +413,7 @@ export namespace Worktree {
         await runStartScripts(info.directory, { projectID, extra })
       }
 
-      void start().catch((error) => {
+      return start().catch((error) => {
         log.error("worktree start task failed", { directory: info.directory, error })
       })
     }

+ 199 - 0
packages/opencode/test/mcp/oauth-auto-connect.test.ts

@@ -0,0 +1,199 @@
+import { test, expect, mock, beforeEach } from "bun:test"
+
+// Mock UnauthorizedError to match the SDK's class
+class MockUnauthorizedError extends Error {
+  constructor(message?: string) {
+    super(message ?? "Unauthorized")
+    this.name = "UnauthorizedError"
+  }
+}
+
+// Track what options were passed to each transport constructor
+const transportCalls: Array<{
+  type: "streamable" | "sse"
+  url: string
+  options: { authProvider?: unknown }
+}> = []
+
+// Controls whether the mock transport simulates a 401 that triggers the SDK
+// auth flow (which calls provider.state()) or a simple UnauthorizedError.
+let simulateAuthFlow = true
+
+// Mock the transport constructors to simulate OAuth auto-auth on 401
+mock.module("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
+  StreamableHTTPClientTransport: class MockStreamableHTTP {
+    authProvider:
+      | {
+          state?: () => Promise<string>
+          redirectToAuthorization?: (url: URL) => Promise<void>
+          saveCodeVerifier?: (v: string) => Promise<void>
+        }
+      | undefined
+    constructor(url: URL, options?: { authProvider?: unknown }) {
+      this.authProvider = options?.authProvider as typeof this.authProvider
+      transportCalls.push({
+        type: "streamable",
+        url: url.toString(),
+        options: options ?? {},
+      })
+    }
+    async start() {
+      // Simulate what the real SDK transport does on 401:
+      // It calls auth() which eventually calls provider.state(), then
+      // provider.redirectToAuthorization(), then throws UnauthorizedError.
+      if (simulateAuthFlow && this.authProvider) {
+        // The SDK calls provider.state() to get the OAuth state parameter
+        if (this.authProvider.state) {
+          await this.authProvider.state()
+        }
+        // The SDK calls saveCodeVerifier before redirecting
+        if (this.authProvider.saveCodeVerifier) {
+          await this.authProvider.saveCodeVerifier("test-verifier")
+        }
+        // The SDK calls redirectToAuthorization to redirect the user
+        if (this.authProvider.redirectToAuthorization) {
+          await this.authProvider.redirectToAuthorization(new URL("https://auth.example.com/authorize?state=test"))
+        }
+        throw new MockUnauthorizedError()
+      }
+      throw new MockUnauthorizedError()
+    }
+    async finishAuth(_code: string) {}
+  },
+}))
+
+mock.module("@modelcontextprotocol/sdk/client/sse.js", () => ({
+  SSEClientTransport: class MockSSE {
+    constructor(url: URL, options?: { authProvider?: unknown }) {
+      transportCalls.push({
+        type: "sse",
+        url: url.toString(),
+        options: options ?? {},
+      })
+    }
+    async start() {
+      throw new Error("Mock SSE transport cannot connect")
+    }
+  },
+}))
+
+// Mock the MCP SDK Client
+mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({
+  Client: class MockClient {
+    async connect(transport: { start: () => Promise<void> }) {
+      await transport.start()
+    }
+  },
+}))
+
+// Mock UnauthorizedError in the auth module so instanceof checks work
+mock.module("@modelcontextprotocol/sdk/client/auth.js", () => ({
+  UnauthorizedError: MockUnauthorizedError,
+}))
+
+beforeEach(() => {
+  transportCalls.length = 0
+  simulateAuthFlow = true
+})
+
+// Import modules after mocking
+const { MCP } = await import("../../src/mcp/index")
+const { Instance } = await import("../../src/project/instance")
+const { tmpdir } = await import("../fixture/fixture")
+
+test("first connect to OAuth server shows needs_auth instead of failed", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      await Bun.write(
+        `${dir}/opencode.json`,
+        JSON.stringify({
+          $schema: "https://opencode.ai/config.json",
+          mcp: {
+            "test-oauth": {
+              type: "remote",
+              url: "https://example.com/mcp",
+            },
+          },
+        }),
+      )
+    },
+  })
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const result = await MCP.add("test-oauth", {
+        type: "remote",
+        url: "https://example.com/mcp",
+      })
+
+      const serverStatus = result.status as Record<string, { status: string; error?: string }>
+
+      // The server should be detected as needing auth, NOT as failed.
+      // Before the fix, provider.state() would throw a plain Error
+      // ("No OAuth state saved for MCP server: test-oauth") which was
+      // not caught as UnauthorizedError, causing status to be "failed".
+      expect(serverStatus["test-oauth"]).toBeDefined()
+      expect(serverStatus["test-oauth"].status).toBe("needs_auth")
+    },
+  })
+})
+
+test("state() generates a new state when none is saved", async () => {
+  const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider")
+  const { McpAuth } = await import("../../src/mcp/auth")
+
+  await using tmp = await tmpdir()
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const provider = new McpOAuthProvider(
+        "test-state-gen",
+        "https://example.com/mcp",
+        {},
+        { onRedirect: async () => {} },
+      )
+
+      // Ensure no state exists
+      const entryBefore = await McpAuth.get("test-state-gen")
+      expect(entryBefore?.oauthState).toBeUndefined()
+
+      // state() should generate and return a new state, not throw
+      const state = await provider.state()
+      expect(typeof state).toBe("string")
+      expect(state.length).toBe(64) // 32 bytes as hex
+
+      // The generated state should be persisted
+      const entryAfter = await McpAuth.get("test-state-gen")
+      expect(entryAfter?.oauthState).toBe(state)
+    },
+  })
+})
+
+test("state() returns existing state when one is saved", async () => {
+  const { McpOAuthProvider } = await import("../../src/mcp/oauth-provider")
+  const { McpAuth } = await import("../../src/mcp/auth")
+
+  await using tmp = await tmpdir()
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const provider = new McpOAuthProvider(
+        "test-state-existing",
+        "https://example.com/mcp",
+        {},
+        { onRedirect: async () => {} },
+      )
+
+      // Pre-save a state
+      const existingState = "pre-saved-state-value"
+      await McpAuth.updateOAuthState("test-state-existing", existingState)
+
+      // state() should return the existing state
+      const state = await provider.state()
+      expect(state).toBe(existingState)
+    },
+  })
+})

+ 2 - 0
packages/opencode/test/provider/provider.test.ts

@@ -260,6 +260,7 @@ test("env variable takes precedence, config merges options", async () => {
             anthropic: {
               options: {
                 timeout: 60000,
+                chunkTimeout: 15000,
               },
             },
           },
@@ -277,6 +278,7 @@ test("env variable takes precedence, config merges options", async () => {
       expect(providers["anthropic"]).toBeDefined()
       // Config options should be merged
       expect(providers["anthropic"].options.timeout).toBe(60000)
+      expect(providers["anthropic"].options.chunkTimeout).toBe(15000)
     },
   })
 })

+ 140 - 0
packages/opencode/test/provider/transform.test.ts

@@ -2479,4 +2479,144 @@ describe("ProviderTransform.variants", () => {
       expect(result).toEqual({})
     })
   })
+
+  describe("@jerome-benoit/sap-ai-provider-v2", () => {
+    test("anthropic models return thinking variants", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/anthropic--claude-sonnet-4",
+        providerID: "sap-ai-core",
+        api: {
+          id: "anthropic--claude-sonnet-4",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(Object.keys(result)).toEqual(["high", "max"])
+      expect(result.high).toEqual({
+        thinking: {
+          type: "enabled",
+          budgetTokens: 16000,
+        },
+      })
+      expect(result.max).toEqual({
+        thinking: {
+          type: "enabled",
+          budgetTokens: 31999,
+        },
+      })
+    })
+
+    test("anthropic 4.6 models return adaptive thinking variants", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/anthropic--claude-sonnet-4-6",
+        providerID: "sap-ai-core",
+        api: {
+          id: "anthropic--claude-sonnet-4-6",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
+      expect(result.low).toEqual({
+        thinking: {
+          type: "adaptive",
+        },
+        effort: "low",
+      })
+      expect(result.max).toEqual({
+        thinking: {
+          type: "adaptive",
+        },
+        effort: "max",
+      })
+    })
+
+    test("gemini 2.5 models return thinkingConfig variants", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/gcp--gemini-2.5-pro",
+        providerID: "sap-ai-core",
+        api: {
+          id: "gcp--gemini-2.5-pro",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(Object.keys(result)).toEqual(["high", "max"])
+      expect(result.high).toEqual({
+        thinkingConfig: {
+          includeThoughts: true,
+          thinkingBudget: 16000,
+        },
+      })
+      expect(result.max).toEqual({
+        thinkingConfig: {
+          includeThoughts: true,
+          thinkingBudget: 24576,
+        },
+      })
+    })
+
+    test("gpt models return reasoningEffort variants", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/azure-openai--gpt-4o",
+        providerID: "sap-ai-core",
+        api: {
+          id: "azure-openai--gpt-4o",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(Object.keys(result)).toEqual(["low", "medium", "high"])
+      expect(result.low).toEqual({ reasoningEffort: "low" })
+      expect(result.high).toEqual({ reasoningEffort: "high" })
+    })
+
+    test("o-series models return reasoningEffort variants", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/azure-openai--o3-mini",
+        providerID: "sap-ai-core",
+        api: {
+          id: "azure-openai--o3-mini",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(Object.keys(result)).toEqual(["low", "medium", "high"])
+      expect(result.low).toEqual({ reasoningEffort: "low" })
+      expect(result.high).toEqual({ reasoningEffort: "high" })
+    })
+
+    test("sonar models return empty object", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/perplexity--sonar-pro",
+        providerID: "sap-ai-core",
+        api: {
+          id: "perplexity--sonar-pro",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(result).toEqual({})
+    })
+
+    test("mistral models return empty object", () => {
+      const model = createMockModel({
+        id: "sap-ai-core/mistral--mistral-large",
+        providerID: "sap-ai-core",
+        api: {
+          id: "mistral--mistral-large",
+          url: "https://api.ai.sap",
+          npm: "@jerome-benoit/sap-ai-provider-v2",
+        },
+      })
+      const result = ProviderTransform.variants(model)
+      expect(result).toEqual({})
+    })
+  })
 })

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

@@ -1295,6 +1295,7 @@ export class Session2 extends HeyApiClient {
       parentID?: string
       title?: string
       permission?: PermissionRuleset
+      workspaceID?: string
     },
     options?: Options<never, ThrowOnError>,
   ) {
@@ -1308,6 +1309,7 @@ export class Session2 extends HeyApiClient {
             { in: "body", key: "parentID" },
             { in: "body", key: "title" },
             { in: "body", key: "permission" },
+            { in: "body", key: "workspaceID" },
           ],
         },
       ],

+ 6 - 1
packages/sdk/js/src/v2/gen/types.gen.ts

@@ -1225,7 +1225,11 @@ export type ProviderConfig = {
      * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
      */
     timeout?: number | false
-    [key: string]: unknown | string | boolean | number | false | undefined
+    /**
+     * Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.
+     */
+    chunkTimeout?: number
+    [key: string]: unknown | string | boolean | number | false | number | undefined
   }
 }
 
@@ -2764,6 +2768,7 @@ export type SessionCreateData = {
     parentID?: string
     title?: string
     permission?: PermissionRuleset
+    workspaceID?: string
   }
   path?: never
   query?: {

+ 10 - 0
packages/sdk/openapi.json

@@ -1832,6 +1832,10 @@
                   },
                   "permission": {
                     "$ref": "#/components/schemas/PermissionRuleset"
+                  },
+                  "workspaceID": {
+                    "type": "string",
+                    "pattern": "^wrk.*"
                   }
                 }
               }
@@ -10108,6 +10112,12 @@
                     "const": false
                   }
                 ]
+              },
+              "chunkTimeout": {
+                "description": "Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
+                "type": "integer",
+                "exclusiveMinimum": 0,
+                "maximum": 9007199254740991
               }
             },
             "additionalProperties": {}

+ 1 - 1
packages/web/src/content/docs/ar/ecosystem.mdx

@@ -32,7 +32,7 @@ description: مشاريع وتكاملات مبنية باستخدام OpenCode.
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | تعليمات لأوامر shell غير التفاعلية - تمنع التعليق الناتج عن العمليات المعتمدة على TTY                          |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | تتبع استخدام OpenCode باستخدام Wakatime                                                                        |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | تنظيف جداول markdown التي تنتجها LLMs                                                                          |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | تحرير الكود أسرع بـ 10 مرات باستخدام Morph Fast Apply API وعلامات التحرير الكسولة                              |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | تحرير Fast Apply وبحث WarpGrep في قاعدة الكود وضغط السياق عبر Morph                                            |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | وكلاء الخلفية، وأدوات LSP/AST/MCP المعدة مسبقًا، ووكلاء مختارون، متوافق مع Claude Code                         |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | إشعارات سطح المكتب وتنبيهات صوتية لجلسات OpenCode                                                              |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | إشعارات سطح المكتب وتنبيهات صوتية لأحداث الإذن والاكتمال والخطأ                                                |

+ 1 - 1
packages/web/src/content/docs/bs/ecosystem.mdx

@@ -32,7 +32,7 @@ Također možete pogledati [awesome-opencode](https://github.com/awesome-opencod
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Upute za neinteraktivne naredbe ljuske - sprječava visi od TTY ovisnih operacija                              |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Pratite upotrebu OpenCode sa Wakatime                                                                         |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Očistite tabele umanjenja vrijednosti koje su izradili LLM                                                    |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10x brže uređivanje koda s Morph Fast Apply API-jem i markerima za lijeno uređivanje                          |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply uređivanje, WarpGrep pretraga koda i kompresija konteksta putem Morph-a                            |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Pozadinski agenti, unapred izgrađeni LSP/AST/MCP alati, kurirani agenti, kompatibilni sa Claude Code          |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Obavještenja na radnoj površini i zvučna upozorenja za OpenCode sesije                                        |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Obavještenja na radnoj površini i zvučna upozorenja za dozvole, završetak i događaje greške                   |

+ 3 - 1
packages/web/src/content/docs/config.mdx

@@ -244,7 +244,7 @@ You can configure the providers and models you want to use in your OpenCode conf
 
 The `small_model` option configures a separate model for lightweight tasks like title generation. By default, OpenCode tries to use a cheaper model if one is available from your provider, otherwise it falls back to your main model.
 
-Provider options can include `timeout` and `setCacheKey`:
+Provider options can include `timeout`, `chunkTimeout`, and `setCacheKey`:
 
 ```json title="opencode.json"
 {
@@ -253,6 +253,7 @@ Provider options can include `timeout` and `setCacheKey`:
     "anthropic": {
       "options": {
         "timeout": 600000,
+        "chunkTimeout": 30000,
         "setCacheKey": true
       }
     }
@@ -261,6 +262,7 @@ Provider options can include `timeout` and `setCacheKey`:
 ```
 
 - `timeout` - Request timeout in milliseconds (default: 300000). Set to `false` to disable.
+- `chunkTimeout` - Timeout in milliseconds between streamed response chunks. If no chunk arrives in time, the request is aborted.
 - `setCacheKey` - Ensure a cache key is always set for designated provider.
 
 You can also configure [local models](/docs/models#local). [Learn more](/docs/models).

+ 1 - 1
packages/web/src/content/docs/da/ecosystem.mdx

@@ -32,7 +32,7 @@ Du kan også tjekke [awesome-opencode](https://github.com/awesome-opencode/aweso
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instruktioner til ikke-interaktive shell-kommandoer - forhindrer hænger fra TTY-afhængige operationer             |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Spor OpenCode brug med Wakatime                                                                                   |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Ryd op afmærkningstabeller produceret af LLMs                                                                     |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10x hurtigere koderedigering med Morph Fast Apply API og dovne redigeringsmarkører                                |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply-redigering, WarpGrep-kodesøgning og kontekstkomprimering via Morph                                     |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Baggrundsagenter, præbyggede LSP/AST/MCP værktøjer, kuraterede agenter, Claude Kodekompatibel                     |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Skrivebordsmeddelelser og lydadvarsler for OpenCode-sessioner                                                     |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Skrivebordsmeddelelser og lydadvarsler for tilladelser, fuldførelse og fejlhændelser                              |

+ 1 - 1
packages/web/src/content/docs/de/ecosystem.mdx

@@ -32,7 +32,7 @@ Sie können sich auch [awesome-opencode](https://github.com/awesome-opencode/awe
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Anweisungen für nicht interaktive Shell-Befehle – verhindert Abstürze bei TTY-abhängigen Vorgängen                            |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Verfolgen Sie die Nutzung von OpenCode mit Wakatime                                                                           |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Von LLMs erstellte Abschriftentabellen bereinigen                                                                             |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10x schnellere Codebearbeitung mit Morph Fast Apply API und Lazy-Edit-Markern                                                 |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply-Bearbeitung, WarpGrep-Codesuche und Kontextkomprimierung über Morph                                                |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Hintergrundagenten, vorgefertigte LSP/AST/MCP-Tools, kuratierte Agenten, Claude Code-kompatibel                               |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Desktop-Benachrichtigungen und akustische Warnungen für OpenCode-Sitzungen                                                    |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Desktop-Benachrichtigungen und akustische Warnungen für Berechtigungs-, Abschluss- und Fehlerereignisse                       |

+ 1 - 1
packages/web/src/content/docs/es/ecosystem.mdx

@@ -32,7 +32,7 @@ También puedes consultar [awesome-opencode](https://github.com/awesome-opencode
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instrucciones para comandos de shell no interactivos: evita bloqueos de operaciones dependientes de TTY                     |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Seguimiento del uso de OpenCode con Wakatime                                                                                |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Limpiar tablas de Markdown producidas por LLMs                                                                              |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Edición de código 10 veces más rápida con Morph Fast Apply API y marcadores de edición diferidos                            |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Edición Fast Apply, búsqueda de código con WarpGrep y compactación de contexto a través de Morph                            |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Agentes en segundo plano, herramientas LSP/AST/MCP prediseñadas, agentes seleccionados, compatible con Claude Code          |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Notificaciones de escritorio y alertas sonoras para sesiones OpenCode                                                       |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Notificaciones de escritorio y alertas sonoras para eventos de permiso, finalización y error                                |

+ 1 - 1
packages/web/src/content/docs/fr/ecosystem.mdx

@@ -32,7 +32,7 @@ Vous pouvez également consulter [awesome-opencode](https://github.com/awesome-o
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instructions pour les commandes shell non interactives - empêche les blocages dus aux opérations dépendantes du TTY                    |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Suit l'utilisation d'OpenCode avec Wakatime                                                                                            |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Nettoie les tableaux Markdown produits par les LLM                                                                                     |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Édition de code 10x plus rapide avec l'API Morph Fast Apply et des marqueurs d'édition différée                                        |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Édition Fast Apply, recherche de code WarpGrep et compaction de contexte via Morph                                                     |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Agents d'arrière-plan, outils LSP/AST/MCP pré-construits, agents sélectionnés, compatible Claude Code                                  |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Notifications de bureau et alertes sonores pour les sessions OpenCode                                                                  |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Notifications de bureau et alertes sonores pour les événements de permission, d'achèvement et d'erreur                                 |

+ 1 - 1
packages/web/src/content/docs/it/ecosystem.mdx

@@ -32,7 +32,7 @@ Puoi anche dare un'occhiata a [awesome-opencode](https://github.com/awesome-open
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Istruzioni per comandi shell non interattivi: evita blocchi dovuti a operazioni dipendenti da TTY |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Traccia l'uso di OpenCode con Wakatime                                                            |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Ripulisce le tabelle markdown prodotte dai LLM                                                    |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Editing del codice 10x più veloce con Morph Fast Apply API e marker lazy edit                     |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Editing Fast Apply, ricerca codebase WarpGrep e compattazione del contesto tramite Morph          |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Agenti in background, tool LSP/AST/MCP predefiniti, agenti curati, compatibile con Claude Code    |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Notifiche desktop e avvisi sonori per le sessioni OpenCode                                        |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Notifiche desktop e avvisi sonori per eventi di permesso, completamento ed errore                 |

+ 1 - 1
packages/web/src/content/docs/ja/ecosystem.mdx

@@ -32,7 +32,7 @@ OpenCode 関連プロジェクトをこのリストに追加したいですか?
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | 非対話型シェルコマンドの手順 - TTY に依存する操作によるハングの防止                                                  |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | wakatime で OpenCode の使用状況を追跡する                                                                            |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | LLM によって生成された Markdown テーブルをクリーンアップする                                                         |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Morph Fast apply API と遅延編集マーカーにより 10 倍高速なコード編集                                                  |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Morph による Fast Apply 編集、WarpGrep コードベース検索、コンテキスト圧縮                                            |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | バックグラウンドエージェント、事前構築された LSP/AST/MCP ツール、厳選されたエージェント、Claude Code 互換            |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | OpenCode セッションのデスクトップ通知とサウンドアラート                                                              |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | 許可、完了、エラーイベントのデスクトップ通知とサウンドアラート                                                       |

+ 1 - 1
packages/web/src/content/docs/ko/ecosystem.mdx

@@ -32,7 +32,7 @@ OpenCode를 기반으로 만들어진 커뮤니티 프로젝트 모음입니다.
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | 비대화형 shell 명령 실행 지침을 제공해 TTY 의존 작업으로 인한 멈춤을 방지합니다.                |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Wakatime으로 OpenCode 사용량을 추적합니다.                                                      |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | LLM이 생성한 markdown 표를 정리합니다.                                                          |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Morph Fast Apply API와 lazy edit marker를 활용해 코드 편집 속도를 크게 높입니다.                |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Morph를 통한 Fast Apply 편집, WarpGrep 코드베이스 검색 및 컨텍스트 압축                         |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | background agent, 사전 구성된 LSP/AST/MCP tool, curated agent, Claude Code 호환성을 제공합니다. |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | OpenCode 세션에 데스크톱 알림과 사운드 알림을 제공합니다.                                       |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | permission, 완료, 오류 이벤트에 대한 데스크톱 알림과 사운드 알림을 제공합니다.                  |

+ 1 - 1
packages/web/src/content/docs/nb/ecosystem.mdx

@@ -32,7 +32,7 @@ Du kan også sjekke ut [awesome-opencode](https://github.com/awesome-opencode/aw
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instruksjoner for ikke-interaktive skallkommandoer - forhindrer heng ved TTY-avhengige operasjoner            |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Spor OpenCode-bruk med Wakatime                                                                               |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Rydd opp i markdown-tabeller produsert av LLMs                                                                |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10 ganger raskere koderedigering med Morph Fast Apply API og lazy-redigeringsmarkører                         |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply-redigering, WarpGrep-kodesøk og kontekstkomprimering via Morph                                     |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Bakgrunnsagenter, forhåndsbygde LSP/AST/MCP verktøy, kurerte agenter, Claude Code-kompatibel                  |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Skrivebordsvarsler og lydvarsler for OpenCode-økter                                                           |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Skrivebordsvarsler og lydvarsler for tillatelse, fullføring og feilhendelser                                  |

+ 1 - 1
packages/web/src/content/docs/pl/ecosystem.mdx

@@ -32,7 +32,7 @@ Możesz również sprawdzić [awesome-opencode](https://github.com/awesome-openc
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instrukcje dla nieinteraktywnych poleceń powłoki - zapobiega zawieszeniom operacji zależnych od TTY                   |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Śledź użycie OpenCode za pomocą Wakatime                                                                              |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Oczyść tabele markdown generowane przez LLM                                                                           |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 10x szybsza edycja kodu dzięki API Morph Fast Apply i leniwym znacznikom edycji                                       |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Edycja Fast Apply, wyszukiwanie kodu WarpGrep i kompaktowanie kontekstu przez Morph                                   |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Agenci w tle, wbudowane narzędzia LSP/AST/MCP, wyselekcjonowani agenci, kompatybilność z Claude Code                  |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Powiadomienia na pulpicie i alerty dźwiękowe dla sesji OpenCode                                                       |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Powiadomienia na pulpicie i alerty dźwiękowe dla uprawnień, zakończeń zadań i błędów                                  |

+ 1 - 1
packages/web/src/content/docs/pt-br/ecosystem.mdx

@@ -32,7 +32,7 @@ Você também pode conferir [awesome-opencode](https://github.com/awesome-openco
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Instruções para comandos de shell não interativos - evita travamentos de operações dependentes de TTY                          |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Acompanhe o uso do OpenCode com Wakatime                                                                                       |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Limpe tabelas markdown produzidas por LLMs                                                                                     |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Edição de código 10x mais rápida com a API Morph Fast Apply e marcadores de edição preguiçosos                                 |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Edição Fast Apply, busca de código WarpGrep e compactação de contexto via Morph                                                |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Agentes em segundo plano, ferramentas LSP/AST/MCP pré-construídas, agentes curados, compatível com Claude Code                 |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Notificações de desktop e alertas sonoros para sessões do OpenCode                                                             |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Notificações de desktop e alertas sonoros para eventos de permissão, conclusão e erro                                          |

+ 1 - 1
packages/web/src/content/docs/ru/ecosystem.mdx

@@ -32,7 +32,7 @@ description: Проекты и интеграции, созданные с по
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Инструкции для неинтерактивных shell-команд — предотвращают зависания из-за операций, зависящих от TTY                                           |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Отслеживайте использование OpenCode с помощью Wakatime                                                                                           |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | Очистка таблиц Markdown, созданных LLM                                                                                                           |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Редактирование кода в 10 раз быстрее с помощью API Morph Fast Apply и маркеров отложенного редактирования                                        |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Редактирование Fast Apply, поиск по кодовой базе WarpGrep и сжатие контекста через Morph                                                         |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Фоновые агенты, встроенные инструменты LSP/AST/MCP, курируемые агенты, совместимость с Claude Code                                               |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | Уведомления на рабочем столе и звуковые оповещения для сеансов OpenCode                                                                          |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | Уведомления на рабочем столе и звуковые оповещения о разрешениях, завершении и событиях ошибок                                                   |

+ 1 - 1
packages/web/src/content/docs/th/ecosystem.mdx

@@ -32,7 +32,7 @@ description: โปรเจ็กต์และการผสานรวม
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | คำแนะนำสำหรับคำสั่ง shell แบบไม่โต้ตอบ - ป้องกันการแฮงค์จากการดำเนินการที่ขึ้นอยู่กับ TTY                             |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | ติดตามการใช้งาน OpenCode ด้วย Wakatime                                                                                |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | ทำความสะอาดตาราง Markdown ที่ผลิตโดย LLM                                                                              |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | การแก้ไขโค้ดเร็วขึ้น 10 เท่าด้วย Morph Fast Apply API และเครื่องหมายแก้ไขแบบ Lazy                                     |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | การแก้ไข Fast Apply, การค้นหาโค้ดเบส WarpGrep และการบีบอัดบริบทผ่าน Morph                                             |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | ตัวแทนเบื้องหลัง, เครื่องมือ LSP/AST/MCP ที่สร้างไว้ล่วงหน้า, ตัวแทนที่ได้รับการดูแลจัดการ, เข้ากันได้กับ Claude Code |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | การแจ้งเตือนบนเดสก์ท็อปและเสียงเตือนสำหรับเซสชัน OpenCode                                                             |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | การแจ้งเตือนบนเดสก์ท็อปและเสียงเตือนสำหรับการอนุญาต การดำเนินการเสร็จสิ้น และเหตุการณ์ข้อผิดพลาด                      |

+ 1 - 1
packages/web/src/content/docs/tr/ecosystem.mdx

@@ -32,7 +32,7 @@ Ayrıca ekosistemi ve topluluğu bir araya getiren [awesome-opencode](https://gi
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | Etkileşimli olmayan kabuk komutları için talimatlar - TTY bağımlı işlemlerden kaynaklanan takılmaları önler                   |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | Wakatime ile OpenCode kullanımını takip edin                                                                                  |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | LLM'ler tarafından üretilen markdown tablolarını temizleyin                                                                   |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | Morph Fast Apply API ve tembel düzenleme işaretçileriyle 10 kat daha hızlı kod düzenleme                                      |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | Fast Apply düzenleme, WarpGrep kod tabanı araması ve Morph ile bağlam sıkıştırma                                              |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | Arka plan aracıları, hazır LSP/AST/MCP araçları, seçilmiş aracılar, Claude Code uyumlu                                        |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | OpenCode oturumları için masaüstü bildirimleri ve sesli uyarılar                                                              |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | İzin, tamamlanma ve hata olayları için masaüstü bildirimleri ve sesli uyarılar                                                |

+ 1 - 1
packages/web/src/content/docs/zh-cn/ecosystem.mdx

@@ -32,7 +32,7 @@ description: 基于 OpenCode 构建的项目与集成。
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | 非交互式 shell 命令指令——防止依赖 TTY 的操作导致挂起                   |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | 使用 Wakatime 追踪 OpenCode 的使用情况                                 |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | 清理 LLM 生成的 Markdown 表格                                          |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 通过 Morph Fast Apply API 和惰性编辑标记实现 10 倍更快的代码编辑       |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | 通过 Morph 提供 Fast Apply 编辑、WarpGrep 代码搜索和上下文压缩         |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | 后台代理、预构建的 LSP/AST/MCP 工具、精选代理,兼容 Claude Code        |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | OpenCode 会话的桌面通知和声音提醒                                      |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | 针对权限请求、任务完成和错误事件的桌面通知与声音提醒                   |

+ 1 - 1
packages/web/src/content/docs/zh-tw/ecosystem.mdx

@@ -32,7 +32,7 @@ description: 基於 OpenCode 建置的專案與整合。
 | [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy)                     | 非互動式 shell 指令說明——防止依賴 TTY 的操作導致卡住                       |
 | [opencode-wakatime](https://github.com/angristan/opencode-wakatime)                                | 使用 Wakatime 追蹤 OpenCode 的使用情況                                     |
 | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main)    | 清理 LLM 生成的 Markdown 表格                                              |
-| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply)                 | 透過 Morph Fast Apply API 和惰性編輯標記實現 10 倍更快的程式碼編輯         |
+| [opencode-morph-plugin](https://github.com/morphllm/opencode-morph-plugin)                         | 透過 Morph 提供 Fast Apply 編輯、WarpGrep 程式碼搜尋和上下文壓縮           |
 | [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)                                   | 背景代理、預建置的 LSP/AST/MCP 工具、精選代理,相容 Claude Code            |
 | [opencode-notificator](https://github.com/panta82/opencode-notificator)                            | OpenCode 工作階段的桌面通知和聲音提醒                                      |
 | [opencode-notifier](https://github.com/mohak34/opencode-notifier)                                  | 針對權限請求、任務完成和錯誤事件的桌面通知與聲音提醒                       |