Jelajahi Sumber

v2 SDK (#5216)

Co-authored-by: GitHub Action <[email protected]>
Dax 4 bulan lalu
induk
melakukan
ea7ec60f51
59 mengubah file dengan 9210 tambahan dan 690 penghapusan
  1. 40 56
      bun.lock
  2. 1 1
      package.json
  3. 13 17
      packages/desktop/src/components/prompt-input.tsx
  4. 8 12
      packages/desktop/src/components/terminal.tsx
  5. 1 1
      packages/desktop/src/context/global-sdk.tsx
  6. 1 1
      packages/desktop/src/context/global-sync.tsx
  7. 5 6
      packages/desktop/src/context/local.tsx
  8. 1 1
      packages/desktop/src/context/sdk.tsx
  9. 7 8
      packages/desktop/src/context/session.tsx
  10. 5 5
      packages/desktop/src/context/sync.tsx
  11. 1 1
      packages/desktop/src/pages/layout.tsx
  12. 67 80
      packages/opencode/src/acp/agent.ts
  13. 6 8
      packages/opencode/src/acp/session.ts
  14. 1 1
      packages/opencode/src/acp/types.ts
  15. 1 1
      packages/opencode/src/cli/cmd/acp.ts
  16. 18 21
      packages/opencode/src/cli/cmd/run.ts
  17. 1 1
      packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
  18. 10 24
      packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
  19. 1 3
      packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
  20. 2 6
      packages/opencode/src/cli/cmd/tui/component/dialog-session-rename.tsx
  21. 1 3
      packages/opencode/src/cli/cmd/tui/component/dialog-tag.tsx
  22. 1 3
      packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
  23. 1 1
      packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
  24. 30 44
      packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
  25. 1 1
      packages/opencode/src/cli/cmd/tui/context/keybind.tsx
  26. 7 4
      packages/opencode/src/cli/cmd/tui/context/sdk.tsx
  27. 9 9
      packages/opencode/src/cli/cmd/tui/context/sync.tsx
  28. 4 12
      packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx
  29. 1 1
      packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx
  30. 1 1
      packages/opencode/src/cli/cmd/tui/routes/session/header.tsx
  31. 19 40
      packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
  32. 1 1
      packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
  33. 8 4
      packages/opencode/src/plugin/index.ts
  34. 102 100
      packages/opencode/src/server/server.ts
  35. 1 1
      packages/opencode/src/share/share-next.ts
  36. 252 100
      packages/sdk/js/openapi.json
  37. 5 2
      packages/sdk/js/package.json
  38. 5 3
      packages/sdk/js/script/build.ts
  39. 7 6
      packages/sdk/js/src/client.ts
  40. 111 0
      packages/sdk/js/src/gen/core/queryKeySerializer.gen.ts
  41. 30 0
      packages/sdk/js/src/v2/client.ts
  42. 18 0
      packages/sdk/js/src/v2/gen/client.gen.ts
  43. 278 0
      packages/sdk/js/src/v2/gen/client/client.gen.ts
  44. 25 0
      packages/sdk/js/src/v2/gen/client/index.ts
  45. 202 0
      packages/sdk/js/src/v2/gen/client/types.gen.ts
  46. 289 0
      packages/sdk/js/src/v2/gen/client/utils.gen.ts
  47. 41 0
      packages/sdk/js/src/v2/gen/core/auth.gen.ts
  48. 82 0
      packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts
  49. 169 0
      packages/sdk/js/src/v2/gen/core/params.gen.ts
  50. 167 0
      packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts
  51. 111 0
      packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts
  52. 237 0
      packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts
  53. 86 0
      packages/sdk/js/src/v2/gen/core/types.gen.ts
  54. 137 0
      packages/sdk/js/src/v2/gen/core/utils.gen.ts
  55. 2343 0
      packages/sdk/js/src/v2/gen/sdk.gen.ts
  56. 3845 0
      packages/sdk/js/src/v2/gen/types.gen.ts
  57. 21 0
      packages/sdk/js/src/v2/index.ts
  58. 120 0
      packages/sdk/js/src/v2/server.ts
  59. 252 100
      packages/sdk/openapi.json

+ 40 - 56
bun.lock

@@ -324,7 +324,7 @@
       "name": "@opencode-ai/sdk",
       "version": "1.0.134",
       "devDependencies": {
-        "@hey-api/openapi-ts": "0.81.0",
+        "@hey-api/openapi-ts": "0.88.1",
         "@tsconfig/node22": "catalog:",
         "@types/node": "catalog:",
         "@typescript/native-preview": "catalog:",
@@ -468,7 +468,7 @@
     "@types/bun": "1.3.3",
     "@types/luxon": "3.7.1",
     "@types/node": "22.13.9",
-    "@typescript/native-preview": "7.0.0-dev.20251014.1",
+    "@typescript/native-preview": "7.0.0-dev.20251207.1",
     "ai": "5.0.97",
     "diff": "8.0.2",
     "fuzzysort": "3.1.0",
@@ -851,9 +851,11 @@
 
     "@happy-dom/global-registrator": ["@happy-dom/[email protected]", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
 
-    "@hey-api/json-schema-ref-parser": ["@hey-api/[email protected]", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } }, "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w=="],
+    "@hey-api/codegen-core": ["@hey-api/[email protected]", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
 
-    "@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-PoJukNBkUfHOoMDpN33bBETX49TUhy7Hu8Sa0jslOvFndvZ5VjQr4Nl/Dzjb9LG1Lp5HjybyTJMA6a1zYk/q6A=="],
+    "@hey-api/json-schema-ref-parser": ["@hey-api/[email protected]", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="],
+
+    "@hey-api/openapi-ts": ["@hey-api/[email protected]", "", { "dependencies": { "@hey-api/codegen-core": "^0.3.3", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-x/nDTupOnV9VuSeNIiJpgIpc915GHduhyseJeMTnI0JMsXaObmpa0rgPr3ASVEYMLgpvqozIEG1RTOOnal6zLQ=="],
 
     "@hono/standard-validator": ["@hono/[email protected]", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
 
@@ -1763,21 +1765,21 @@
 
     "@types/yargs-parser": ["@types/[email protected]", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
 
-    "@typescript/native-preview": ["@typescript/[email protected]014.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20251014.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20251014.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-IqmX5CYCBqXbfL+HKlcQAMaDlfJ0Z8OhUxvADFV2TENnzSYI4CuhvKxwOB2wFSLXufVsgtAlf3Fjwn24KmMyPQ=="],
+    "@typescript/native-preview": ["@typescript/[email protected]207.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251207.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251207.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20251207.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251207.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20251207.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251207.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20251207.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-4QcRnzB0pi9rS0AOvg8kWbmuwHv5X7B2EXHbgcms9+56hsZ8SZrZjNgBJb2rUIodJ4kU5mrkj/xlTTT4r9VcpQ=="],
 
-    "@typescript/native-preview-darwin-arm64": ["@typescript/[email protected]014.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7rQoLlerWnwnvrM56hP4rdEbo4xDE4zr7cch+EzgENq/tbXYereGq1fmnR83UNglb1Eyy53OvJZ3O2csYBa2vg=="],
+    "@typescript/native-preview-darwin-arm64": ["@typescript/[email protected]207.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-waWJnuuvkXh4WdpbTjYf7pyahJzx0ycesV2BylyHrE9OxU9FSKcD/cRLQYvbq3YcBSdF7sZwRLDBer7qTeLsYA=="],
 
-    "@typescript/native-preview-darwin-x64": ["@typescript/[email protected]014.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-SF29o9NFRGDM23Jz0nVO4/yS78GQ81rtOemmCVNXuJotoY4bP3npGDyEmfkZQHZgDOXogs2OWy3t7NUJ235ANQ=="],
+    "@typescript/native-preview-darwin-x64": ["@typescript/[email protected]207.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-3bkD9QuIjxETtp6J1l5X2oKgudJ8z+8fwUq0izCjK1JrIs2vW1aQnbzxhynErSyHWH7URGhHHzcsXHbikckAsg=="],
 
-    "@typescript/native-preview-linux-arm": ["@typescript/[email protected]014.1", "", { "os": "linux", "cpu": "arm" }, "sha512-o5cu7h+BBAp6V4qxYY5RWuaYouN3j+MGFLrrUtvvNj4XKM+kbq5qwsgVRsmJZ1LfUvHmzyQs86vt9djAWedzjQ=="],
+    "@typescript/native-preview-linux-arm": ["@typescript/[email protected]207.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OjrZBq8XJkB7uCQvT1AZ1FPsp+lT0cHxY5SisE+ZTAU6V0IHAZMwJ7J/mnwlGsBcCKRLBT+lX3hgEuOTSwHr9w=="],
 
-    "@typescript/native-preview-linux-arm64": ["@typescript/[email protected]014.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+YWbW/JF4uggEUBr+vflqI5i7bL4Z3XInCOyUO1qQEY7VmfDCsPEzIwGi37O1mixfxw9Qj8LQsptCkU+fqKwGw=="],
+    "@typescript/native-preview-linux-arm64": ["@typescript/[email protected]207.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qhp06OObkwy5B+PlAhAmq+Ls3GVt4LHAovrTRcpLB3Mk3yJ0h9DnIQwPQiayp16TdvTsGHI3jdIX4MGm5L/ghA=="],
 
-    "@typescript/native-preview-linux-x64": ["@typescript/[email protected]014.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3LC4tgcgi6zWJWBUpBNXOGSY3yISJrQezSP/T+v+mQRApkdoIpTSHIyQAhgaagcs3MOQRaqiIPaLOVrdHXdU6A=="],
+    "@typescript/native-preview-linux-x64": ["@typescript/[email protected]207.1", "", { "os": "linux", "cpu": "x64" }, "sha512-fPRw0zfTBeVmrkgi5Le+sSwoeAz6pIdvcsa1OYZcrspueS9hn3qSC5bLEc5yX4NJP1vItadBqyGLUQ7u8FJjow=="],
 
-    "@typescript/native-preview-win32-arm64": ["@typescript/[email protected]014.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-P0D4UEXwzFZh3pHexe2Ky1tW/HjY/HxTBTIajz2ViDCNPw7uDSEsXSB4H9TTiFJw8gVdTUFbsoAQp1MteTeORA=="],
+    "@typescript/native-preview-win32-arm64": ["@typescript/[email protected]207.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-KxY1i+HxeSFfzZ+HVsKwMGBM79laTRZv1ibFqHu22CEsfSPDt4yiV1QFis8Nw7OBXswNqJG/UGqY47VP8FeTvw=="],
 
-    "@typescript/native-preview-win32-x64": ["@typescript/[email protected]014.1", "", { "os": "win32", "cpu": "x64" }, "sha512-fi53g2ihH7tkQLlz8hZGAb2V+3aNZpcxrZ530CQ4xcWwAqssEj0EaZJX0VLEtIQBar1ttGVK9Pz/wJU9sYyVzg=="],
+    "@typescript/native-preview-win32-x64": ["@typescript/[email protected]207.1", "", { "os": "win32", "cpu": "x64" }, "sha512-5l51HlXjX7lXwo65DEl1IaCFLjmkMtL6K3NrSEamPNeNTtTQwZRa3pQ9V65dCglnnCQ0M3+VF1RqzC7FU0iDKg=="],
 
     "@typescript/vfs": ["@typescript/[email protected]", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g=="],
 
@@ -1999,7 +2001,7 @@
 
     "bytes": ["[email protected]", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
 
-    "c12": ["c12@2.0.1", "", { "dependencies": { "chokidar": "^4.0.1", "confbox": "^0.1.7", "defu": "^6.1.4", "dotenv": "^16.4.5", "giget": "^1.2.3", "jiti": "^2.3.0", "mlly": "^1.7.1", "ohash": "^1.1.4", "pathe": "^1.1.2", "perfect-debounce": "^1.0.0", "pkg-types": "^1.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A=="],
+    "c12": ["c12@3.3.2", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A=="],
 
     "call-bind": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
 
@@ -2081,7 +2083,7 @@
 
     "comma-separated-tokens": ["[email protected]", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
 
-    "commander": ["commander@13.0.0", "", {}, "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ=="],
+    "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
 
     "common-ancestor-path": ["[email protected]", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="],
 
@@ -2089,7 +2091,7 @@
 
     "condense-newlines": ["[email protected]", "", { "dependencies": { "extend-shallow": "^2.0.1", "is-whitespace": "^0.3.0", "kind-of": "^3.0.2" } }, "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg=="],
 
-    "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+    "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
 
     "config-chain": ["[email protected]", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="],
 
@@ -2213,7 +2215,7 @@
 
     "dot-prop": ["[email protected]", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
 
-    "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
+    "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
 
     "drizzle-kit": ["[email protected]", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA=="],
 
@@ -2331,6 +2333,8 @@
 
     "expressive-code": ["[email protected]", "", { "dependencies": { "@expressive-code/core": "^0.41.3", "@expressive-code/plugin-frames": "^0.41.3", "@expressive-code/plugin-shiki": "^0.41.3", "@expressive-code/plugin-text-markers": "^0.41.3" } }, "sha512-YLnD62jfgBZYrXIPQcJ0a51Afv9h8VlWqEGK9uU2T5nL/5rb8SnA86+7+mgCZe5D34Tff5RNEA5hjNVJYHzrFg=="],
 
+    "exsolve": ["[email protected]", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
+
     "extend": ["[email protected]", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
 
     "extend-shallow": ["[email protected]", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
@@ -2393,8 +2397,6 @@
 
     "fs-extra": ["[email protected]", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
 
-    "fs-minipass": ["[email protected]", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
-
     "fs.realpath": ["[email protected]", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
 
     "fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
@@ -2441,7 +2443,7 @@
 
     "gifwrap": ["[email protected]", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
 
-    "giget": ["giget@1.2.5", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.5.4", "pathe": "^2.0.3", "tar": "^6.2.1" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug=="],
+    "giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
 
     "github-from-package": ["[email protected]", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
 
@@ -2471,8 +2473,6 @@
 
     "h3": ["[email protected]", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="],
 
-    "handlebars": ["[email protected]", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
-
     "happy-dom": ["[email protected]", "", { "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" } }, "sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g=="],
 
     "has-bigints": ["[email protected]", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
@@ -2645,6 +2645,8 @@
 
     "is-hexadecimal": ["[email protected]", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
 
+    "is-in-ssh": ["[email protected]", "", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="],
+
     "is-inside-container": ["[email protected]", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
 
     "is-map": ["[email protected]", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
@@ -2983,8 +2985,6 @@
 
     "mkdirp-classic": ["[email protected]", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
 
-    "mlly": ["[email protected]", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
-
     "mrmime": ["[email protected]", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
 
     "ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
@@ -3003,8 +3003,6 @@
 
     "negotiator": ["[email protected]", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
 
-    "neo-async": ["[email protected]", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
-
     "neotraverse": ["[email protected]", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
 
     "nf3": ["[email protected]", "", {}, "sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ=="],
@@ -3043,7 +3041,7 @@
 
     "nth-check": ["[email protected]", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
 
-    "nypm": ["nypm@0.5.4", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "tinyexec": "^0.3.2", "ufo": "^1.5.4" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA=="],
+    "nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="],
 
     "object-assign": ["[email protected]", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
 
@@ -3159,7 +3157,7 @@
 
     "peek-readable": ["[email protected]", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="],
 
-    "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
+    "perfect-debounce": ["perfect-debounce@2.0.0", "", {}, "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow=="],
 
     "piccolore": ["[email protected]", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="],
 
@@ -3177,7 +3175,7 @@
 
     "pkg-dir": ["[email protected]", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
 
-    "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
+    "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
 
     "pkg-up": ["[email protected]", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="],
 
@@ -3205,6 +3203,8 @@
 
     "postgres": ["[email protected]", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
 
+    "powershell-utils": ["[email protected]", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
+
     "prebuild-install": ["[email protected]", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
 
     "prettier": ["[email protected]", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
@@ -3459,7 +3459,7 @@
 
     "solid-use": ["[email protected]", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
 
-    "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+    "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
 
     "source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
 
@@ -3659,8 +3659,6 @@
 
     "ufo": ["[email protected]", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
 
-    "uglify-js": ["[email protected]", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
-
     "ulid": ["[email protected]", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q=="],
 
     "ultrahtml": ["[email protected]", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
@@ -3789,8 +3787,6 @@
 
     "widest-line": ["[email protected]", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
 
-    "wordwrap": ["[email protected]", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
-
     "workerd": ["[email protected]", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251118.0", "@cloudflare/workerd-darwin-arm64": "1.20251118.0", "@cloudflare/workerd-linux-64": "1.20251118.0", "@cloudflare/workerd-linux-arm64": "1.20251118.0", "@cloudflare/workerd-windows-64": "1.20251118.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Om5ns0Lyx/LKtYI04IV0bjIrkBgoFNg0p6urzr2asekJlfP18RqFzyqMFZKf0i9Gnjtz/JfAS/Ol6tjCe5JJsQ=="],
 
     "wrangler": ["[email protected]", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.11", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251118.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251118.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251118.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-+nuZuHZxDdKmAyXOSrHlciGshCoAPiy5dM+t6mEohWm7HpXvTHmWQGUf/na9jjWlWJHCJYOWzkA1P5HBJqrIEA=="],
@@ -3803,6 +3799,8 @@
 
     "ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
 
+    "wsl-utils": ["[email protected]", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-3sFIGLiaDP7rTO4xh3g+b3AzhYDIUGGywE/WsmqzJWDxus5aJXVnPTNC/6L+r2WzrwXqVOdD262OaO+cEyPMSQ=="],
+
     "xdg-basedir": ["[email protected]", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
 
     "xml-parse-from-string": ["[email protected]", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="],
@@ -3897,8 +3895,6 @@
 
     "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-hX2cLC/KW74Io1zIbn92kI482j9J7LleBLGCVU9EP3BeH5MVrnFawOnqD0t/q6D1Z+ZNeQG2gNKMslCcO36wng=="],
 
-    "@astrojs/mdx/source-map": ["[email protected]", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
-
     "@astrojs/sitemap/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
 
     "@astrojs/solid-js/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
@@ -3985,6 +3981,10 @@
 
     "@expressive-code/plugin-shiki/shiki": ["[email protected]", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
 
+    "@hey-api/json-schema-ref-parser/js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
+
+    "@hey-api/openapi-ts/open": ["[email protected]", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
+
     "@hono/zod-validator/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
 
     "@isaacs/cliui/string-width": ["[email protected]", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -4031,8 +4031,6 @@
 
     "@jsx-email/doiuse-email/htmlparser2": ["[email protected]", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="],
 
-    "@mdx-js/mdx/source-map": ["[email protected]", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
-
     "@modelcontextprotocol/sdk/express": ["[email protected]", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
 
     "@modelcontextprotocol/sdk/raw-body": ["[email protected]", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
@@ -4177,9 +4175,7 @@
 
     "boxen/chalk": ["[email protected]", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
 
-    "c12/ohash": ["[email protected]", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
-
-    "c12/pathe": ["[email protected]", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
+    "clean-css/source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
 
     "compress-commons/is-stream": ["[email protected]", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
 
@@ -4199,8 +4195,6 @@
 
     "esbuild-plugin-copy/chokidar": ["[email protected]", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
 
-    "estree-util-to-js/source-map": ["[email protected]", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
-
     "execa/is-stream": ["[email protected]", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
 
     "express/cookie": ["[email protected]", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
@@ -4215,14 +4209,10 @@
 
     "form-data/mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
 
-    "fs-minipass/minipass": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
     "gaxios/is-stream": ["[email protected]", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
 
     "gaxios/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
 
-    "giget/tar": ["[email protected]", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
-
     "glob/minimatch": ["[email protected]", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
 
     "globby/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
@@ -4259,6 +4249,8 @@
 
     "npm-run-path/path-key": ["[email protected]", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
 
+    "nypm/tinyexec": ["[email protected]", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
+
     "opencode/@ai-sdk/anthropic": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
 
     "opencode/@ai-sdk/openai": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="],
@@ -4319,6 +4311,8 @@
 
     "sitemap/sax": ["[email protected]", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
 
+    "source-map-support/source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+
     "sst/aws4fetch": ["[email protected]", "", {}, "sha512-3Cf+YaUl07p24MoQ46rFwulAmiyCwH2+1zw1ZyPAX5OtJ34Hh185DwB8y/qRLb6cYYYtSFJ9pthyLc0MD4e8sQ=="],
 
     "sst/jose": ["[email protected]", "", {}, "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA=="],
@@ -4823,14 +4817,6 @@
 
     "form-data/mime-types/mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
 
-    "giget/tar/chownr": ["[email protected]", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
-
-    "giget/tar/minipass": ["[email protected]", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
-
-    "giget/tar/minizlib": ["[email protected]", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
-
-    "giget/tar/mkdirp": ["[email protected]", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
-
     "gray-matter/js-yaml/argparse": ["[email protected]", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
 
     "js-beautify/glob/jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
@@ -5007,8 +4993,6 @@
 
     "esbuild-plugin-copy/chokidar/readdirp/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
 
-    "giget/tar/minizlib/minipass": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
-
     "js-beautify/glob/path-scurry/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
 
     "opencontrol/@modelcontextprotocol/sdk/express/accepts": ["[email protected]", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],

+ 1 - 1
package.json

@@ -39,7 +39,7 @@
       "fuzzysort": "3.1.0",
       "luxon": "3.6.1",
       "typescript": "5.8.2",
-      "@typescript/native-preview": "7.0.0-dev.20251014.1",
+      "@typescript/native-preview": "7.0.0-dev.20251207.1",
       "zod": "4.1.8",
       "remeda": "2.26.0",
       "solid-list": "0.3.0",

+ 13 - 17
packages/desktop/src/components/prompt-input.tsx

@@ -235,9 +235,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
 
   const abort = () =>
     sdk.client.session.abort({
-      path: {
-        id: session.id!,
-      },
+      sessionID: session.id!,
     })
 
   const handleKeyDown = (event: KeyboardEvent) => {
@@ -329,21 +327,19 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
     session.prompt.set([{ type: "text", content: "", start: 0, end: 0 }], 0)
 
     sdk.client.session.prompt({
-      path: { id: existing.id },
-      body: {
-        agent: local.agent.current()!.name,
-        model: {
-          modelID: local.model.current()!.id,
-          providerID: local.model.current()!.provider.id,
-        },
-        parts: [
-          {
-            type: "text",
-            text,
-          },
-          ...attachmentParts,
-        ],
+      sessionID: existing.id,
+      agent: local.agent.current()!.name,
+      model: {
+        modelID: local.model.current()!.id,
+        providerID: local.model.current()!.provider.id,
       },
+      parts: [
+        {
+          type: "text",
+          text,
+        },
+        ...attachmentParts,
+      ],
     })
   }
 

+ 8 - 12
packages/desktop/src/components/terminal.tsx

@@ -74,12 +74,10 @@ export const Terminal = (props: TerminalProps) => {
     term.onResize(async (size) => {
       if (ws && ws.readyState === WebSocket.OPEN) {
         await sdk.client.pty.update({
-          path: { id: local.pty.id },
-          body: {
-            size: {
-              cols: size.cols,
-              rows: size.rows,
-            },
+          ptyID: local.pty.id,
+          size: {
+            cols: size.cols,
+            rows: size.rows,
           },
         })
       }
@@ -100,12 +98,10 @@ export const Terminal = (props: TerminalProps) => {
     ws.addEventListener("open", () => {
       console.log("WebSocket connected")
       sdk.client.pty.update({
-        path: { id: local.pty.id },
-        body: {
-          size: {
-            cols: term.cols,
-            rows: term.rows,
-          },
+        ptyID: local.pty.id,
+        size: {
+          cols: term.cols,
+          rows: term.rows,
         },
       })
     })

+ 1 - 1
packages/desktop/src/context/global-sdk.tsx

@@ -1,4 +1,4 @@
-import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
+import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createGlobalEmitter } from "@solid-primitives/event-bus"
 import { onCleanup } from "solid-js"

+ 1 - 1
packages/desktop/src/context/global-sync.tsx

@@ -12,7 +12,7 @@ import type {
   FileDiff,
   Todo,
   SessionStatus,
-} from "@opencode-ai/sdk"
+} from "@opencode-ai/sdk/v2"
 import { createStore, produce, reconcile } from "solid-js/store"
 import { Binary } from "@opencode-ai/util/binary"
 import { createSimpleContext } from "@opencode-ai/ui/context"

+ 5 - 6
packages/desktop/src/context/local.tsx

@@ -1,7 +1,7 @@
 import { createStore, produce, reconcile } from "solid-js/store"
 import { batch, createEffect, createMemo } from "solid-js"
 import { uniqueBy } from "remeda"
-import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk"
+import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk/v2"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { useSDK } from "./sdk"
 import { useSync } from "./sync"
@@ -257,7 +257,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
 
       const load = async (path: string) => {
         const relativePath = relative(path)
-        sdk.client.file.read({ query: { path: relativePath } }).then((x) => {
+        sdk.client.file.read({ path: relativePath }).then((x) => {
           setStore(
             "node",
             relativePath,
@@ -305,7 +305,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
       }
 
       const list = async (path: string) => {
-        return sdk.client.file.list({ query: { path: path + "/" } }).then((x) => {
+        return sdk.client.file.list({ path: path + "/" }).then((x) => {
           setStore(
             "node",
             produce((draft) => {
@@ -318,10 +318,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
         })
       }
 
-      const searchFiles = (query: string) =>
-        sdk.client.find.files({ query: { query, dirs: "false" } }).then((x) => x.data!)
+      const searchFiles = (query: string) => sdk.client.find.files({ query, dirs: "false" }).then((x) => x.data!)
       const searchFilesAndDirectories = (query: string) =>
-        sdk.client.find.files({ query: { query, dirs: "true" } }).then((x) => x.data!)
+        sdk.client.find.files({ query, dirs: "true" }).then((x) => x.data!)
 
       sdk.event.listen((e) => {
         const event = e.details

+ 1 - 1
packages/desktop/src/context/sdk.tsx

@@ -1,4 +1,4 @@
-import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client"
+import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createGlobalEmitter } from "@solid-primitives/event-bus"
 import { onCleanup } from "solid-js"

+ 7 - 8
packages/desktop/src/context/session.tsx

@@ -5,7 +5,7 @@ import { useSync } from "./sync"
 import { makePersisted } from "@solid-primitives/storage"
 import { TextSelection } from "./local"
 import { pipe, sumBy } from "remeda"
-import { AssistantMessage, UserMessage } from "@opencode-ai/sdk"
+import { AssistantMessage, UserMessage } from "@opencode-ai/sdk/v2"
 import { useParams } from "@solidjs/router"
 import { base64Encode } from "@/utils"
 import { useSDK } from "./sdk"
@@ -198,7 +198,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
         all: createMemo(() => Object.values(store.terminals.all)),
         active: createMemo(() => store.terminals.active),
         new() {
-          sdk.client.pty.create({ body: { title: `Terminal ${store.terminals.all.length + 1}` } }).then((pty) => {
+          sdk.client.pty.create({ title: `Terminal ${store.terminals.all.length + 1}` }).then((pty) => {
             const id = pty.data?.id
             if (!id) return
             setStore("terminals", "all", [
@@ -214,8 +214,9 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
         update(pty: Partial<LocalPTY> & { id: string }) {
           setStore("terminals", "all", (x) => x.map((x) => (x.id === pty.id ? { ...x, ...pty } : x)))
           sdk.client.pty.update({
-            path: { id: pty.id },
-            body: { title: pty.title, size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined },
+            ptyID: pty.id,
+            title: pty.title,
+            size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined,
           })
         },
         async clone(id: string) {
@@ -223,9 +224,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
           const pty = store.terminals.all[index]
           if (!pty) return
           const clone = await sdk.client.pty.create({
-            body: {
-              title: pty.title,
-            },
+            title: pty.title,
           })
           if (!clone.data) return
           setStore("terminals", "all", index, {
@@ -252,7 +251,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
               setStore("terminals", "active", previous)
             }
           })
-          await sdk.client.pty.remove({ path: { id } })
+          await sdk.client.pty.remove({ ptyID: id })
         },
         move(id: string, to: number) {
           const index = store.terminals.all.findIndex((f) => f.id === id)

+ 5 - 5
packages/desktop/src/context/sync.tsx

@@ -28,7 +28,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
       status: () => sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
       config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
       changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)),
-      node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)),
+      node: () => sdk.client.file.list({ path: "/" }).then((x) => setStore("node", x.data!)),
     }
 
     Promise.all(Object.values(load).map((p) => p())).then(() => setStore("ready", true))
@@ -49,10 +49,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
         },
         async sync(sessionID: string, _isRetry = false) {
           const [session, messages, todo, diff] = await Promise.all([
-            sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }),
-            sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }),
-            sdk.client.session.todo({ path: { id: sessionID } }),
-            sdk.client.session.diff({ path: { id: sessionID } }),
+            sdk.client.session.get({ sessionID }, { throwOnError: true }),
+            sdk.client.session.messages({ sessionID, limit: 100 }),
+            sdk.client.session.todo({ sessionID }),
+            sdk.client.session.diff({ sessionID }),
           ])
           setStore(
             produce((draft) => {

+ 1 - 1
packages/desktop/src/pages/layout.tsx

@@ -13,7 +13,7 @@ import { Collapsible } from "@opencode-ai/ui/collapsible"
 import { DiffChanges } from "@opencode-ai/ui/diff-changes"
 import { getFilename } from "@opencode-ai/util/path"
 import { Select } from "@opencode-ai/ui/select"
-import { Session } from "@opencode-ai/sdk/client"
+import { Session } from "@opencode-ai/sdk/v2/client"
 
 export default function Layout(props: ParentProps) {
   const navigate = useNavigate()

+ 67 - 80
packages/opencode/src/acp/agent.ts

@@ -29,7 +29,7 @@ import { MCP } from "@/mcp"
 import { Todo } from "@/session/todo"
 import { z } from "zod"
 import { LoadAPIKeyError } from "ai"
-import type { OpencodeClient } from "@opencode-ai/sdk"
+import type { OpencodeClient } from "@opencode-ai/sdk/v2"
 
 export namespace ACP {
   const log = Log.create({ service: "acp-agent" })
@@ -68,7 +68,7 @@ export namespace ACP {
         { optionId: "always", kind: "allow_always", name: "Always allow" },
         { optionId: "reject", kind: "reject_once", name: "Reject" },
       ]
-      this.config.sdk.event.subscribe({ query: { directory } }).then(async (events) => {
+      this.config.sdk.event.subscribe({ directory }).then(async (events) => {
         for await (const event of events.stream) {
           switch (event.type) {
             case "permission.updated":
@@ -93,32 +93,29 @@ export namespace ACP {
                       permissionID: permission.id,
                       sessionID: permission.sessionID,
                     })
-                    await this.config.sdk.postSessionIdPermissionsPermissionId({
-                      path: { id: permission.sessionID, permissionID: permission.id },
-                      body: {
-                        response: "reject",
-                      },
-                      query: { directory },
+                    await this.config.sdk.permission.respond({
+                      sessionID: permission.sessionID,
+                      permissionID: permission.id,
+                      response: "reject",
+                      directory,
                     })
                     return
                   })
                 if (!res) return
                 if (res.outcome.outcome !== "selected") {
-                  await this.config.sdk.postSessionIdPermissionsPermissionId({
-                    path: { id: permission.sessionID, permissionID: permission.id },
-                    body: {
-                      response: "reject",
-                    },
-                    query: { directory },
+                  await this.config.sdk.permission.respond({
+                    sessionID: permission.sessionID,
+                    permissionID: permission.id,
+                    response: "reject",
+                    directory,
                   })
                   return
                 }
-                await this.config.sdk.postSessionIdPermissionsPermissionId({
-                  path: { id: permission.sessionID, permissionID: permission.id },
-                  body: {
-                    response: res.outcome.optionId as "once" | "always" | "reject",
-                  },
-                  query: { directory },
+                await this.config.sdk.permission.respond({
+                  sessionID: permission.sessionID,
+                  permissionID: permission.id,
+                  response: res.outcome.optionId as "once" | "always" | "reject",
+                  directory,
                 })
               } catch (err) {
                 log.error("unexpected error when handling permission", { error: err })
@@ -133,14 +130,14 @@ export namespace ACP {
                 const { part } = props
 
                 const message = await this.config.sdk.session
-                  .message({
-                    throwOnError: true,
-                    path: {
-                      id: part.sessionID,
+                  .message(
+                    {
+                      sessionID: part.sessionID,
                       messageID: part.messageID,
+                      directory,
                     },
-                    query: { directory },
-                  })
+                    { throwOnError: true },
+                  )
                   .then((x) => x.data)
                   .catch((err) => {
                     log.error("unexpected error when fetching message", { error: err })
@@ -420,9 +417,7 @@ export namespace ACP {
       const model = await defaultModel(this.config, directory)
       const sessionId = params.sessionId
 
-      const providers = await this.sdk.config
-        .providers({ throwOnError: true, query: { directory } })
-        .then((x) => x.data.providers)
+      const providers = await this.sdk.config.providers({ directory }).then((x) => x.data!.providers)
       const entries = providers.sort((a, b) => {
         const nameA = a.name.toLowerCase()
         const nameB = b.name.toLowerCase()
@@ -439,22 +434,22 @@ export namespace ACP {
       })
 
       const agents = await this.config.sdk.app
-        .agents({
-          throwOnError: true,
-          query: {
+        .agents(
+          {
             directory,
           },
-        })
-        .then((resp) => resp.data)
+          { throwOnError: true },
+        )
+        .then((resp) => resp.data!)
 
       const commands = await this.config.sdk.command
-        .list({
-          throwOnError: true,
-          query: {
+        .list(
+          {
             directory,
           },
-        })
-        .then((resp) => resp.data)
+          { throwOnError: true },
+        )
+        .then((resp) => resp.data!)
 
       const availableCommands = commands.map((command) => ({
         name: command.name,
@@ -503,14 +498,14 @@ export namespace ACP {
       await Promise.all(
         Object.entries(mcpServers).map(async ([key, mcp]) => {
           await this.sdk.mcp
-            .add({
-              throwOnError: true,
-              query: { directory },
-              body: {
+            .add(
+              {
+                directory,
                 name: key,
                 config: mcp,
               },
-            })
+              { throwOnError: true },
+            )
             .catch((error) => {
               log.error("failed to add mcp server", { name: key, error })
             })
@@ -559,7 +554,7 @@ export namespace ACP {
     async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse | void> {
       this.sessionManager.get(params.sessionId)
       await this.config.sdk.app
-        .agents({ throwOnError: true })
+        .agents({}, { throwOnError: true })
         .then((x) => x.data)
         .then((agent) => {
           if (!agent) throw new Error(`Agent not found: ${params.modeId}`)
@@ -651,50 +646,42 @@ export namespace ACP {
 
       if (!cmd) {
         await this.sdk.session.prompt({
-          path: { id: sessionID },
-          body: {
-            model: {
-              providerID: model.providerID,
-              modelID: model.modelID,
-            },
-            parts,
-            agent,
-          },
-          query: {
-            directory,
+          sessionID,
+          model: {
+            providerID: model.providerID,
+            modelID: model.modelID,
           },
+          parts,
+          agent,
+          directory,
         })
         return done
       }
 
       const command = await this.config.sdk.command
-        .list({ throwOnError: true, query: { directory } })
-        .then((x) => x.data.find((c) => c.name === cmd.name))
+        .list({ directory }, { throwOnError: true })
+        .then((x) => x.data!.find((c) => c.name === cmd.name))
       if (command) {
         await this.sdk.session.command({
-          path: { id: sessionID },
-          body: {
-            command: command.name,
-            arguments: cmd.args,
-            model: model.providerID + "/" + model.modelID,
-            agent,
-          },
-          query: {
-            directory,
-          },
+          sessionID,
+          command: command.name,
+          arguments: cmd.args,
+          model: model.providerID + "/" + model.modelID,
+          agent,
+          directory,
         })
         return done
       }
 
       switch (cmd.name) {
         case "compact":
-          await this.config.sdk.session.summarize({
-            path: { id: sessionID },
-            throwOnError: true,
-            query: {
+          await this.config.sdk.session.summarize(
+            {
+              sessionID,
               directory,
             },
-          })
+            { throwOnError: true },
+          )
           break
       }
 
@@ -703,13 +690,13 @@ export namespace ACP {
 
     async cancel(params: CancelNotification) {
       const session = this.sessionManager.get(params.sessionId)
-      await this.config.sdk.session.abort({
-        path: { id: params.sessionId },
-        throwOnError: true,
-        query: {
+      await this.config.sdk.session.abort(
+        {
+          sessionID: params.sessionId,
           directory: session.cwd,
         },
-      })
+        { throwOnError: true },
+      )
     }
   }
 
@@ -766,10 +753,10 @@ export namespace ACP {
     if (configured) return configured
 
     const model = await sdk.config
-      .get({ throwOnError: true, query: { directory: cwd } })
+      .get({ directory: cwd }, { throwOnError: true })
       .then((resp) => {
         const cfg = resp.data
-        if (!cfg.model) return undefined
+        if (!cfg || !cfg.model) return undefined
         const parsed = Provider.parseModel(cfg.model)
         return {
           providerID: parsed.providerID,

+ 6 - 8
packages/opencode/src/acp/session.ts

@@ -1,7 +1,7 @@
 import { RequestError, type McpServer } from "@agentclientprotocol/sdk"
 import type { ACPSessionState } from "./types"
 import { Log } from "@/util/log"
-import type { OpencodeClient } from "@opencode-ai/sdk"
+import type { OpencodeClient } from "@opencode-ai/sdk/v2"
 
 const log = Log.create({ service: "acp-session-manager" })
 
@@ -15,16 +15,14 @@ export class ACPSessionManager {
 
   async create(cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"]): Promise<ACPSessionState> {
     const session = await this.sdk.session
-      .create({
-        body: {
+      .create(
+        {
           title: `ACP Session ${crypto.randomUUID()}`,
-        },
-        query: {
           directory: cwd,
         },
-        throwOnError: true,
-      })
-      .then((x) => x.data)
+        { throwOnError: true },
+      )
+      .then((x) => x.data!)
 
     const sessionId = session.id
     const resolvedModel = model

+ 1 - 1
packages/opencode/src/acp/types.ts

@@ -1,5 +1,5 @@
 import type { McpServer } from "@agentclientprotocol/sdk"
-import type { OpencodeClient } from "@opencode-ai/sdk"
+import type { OpencodeClient } from "@opencode-ai/sdk/v2"
 
 export interface ACPSessionState {
   id: string

+ 1 - 1
packages/opencode/src/cli/cmd/acp.ts

@@ -4,7 +4,7 @@ import { cmd } from "./cmd"
 import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
 import { ACP } from "@/acp/agent"
 import { Server } from "@/server/server"
-import { createOpencodeClient } from "@opencode-ai/sdk"
+import { createOpencodeClient } from "@opencode-ai/sdk/v2"
 
 const log = Log.create({ service: "acp-command" })
 

+ 18 - 21
packages/opencode/src/cli/cmd/run.ts

@@ -7,7 +7,7 @@ import { bootstrap } from "../bootstrap"
 import { Command } from "../../command"
 import { EOL } from "os"
 import { select } from "@clack/prompts"
-import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk"
+import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2"
 import { Server } from "../../server/server"
 import { Provider } from "../../provider/provider"
 
@@ -212,9 +212,10 @@ export const RunCommand = cmd({
               initialValue: "once",
             }).catch(() => "reject")
             const response = (result.toString().includes("cancel") ? "reject" : result) as "once" | "always" | "reject"
-            await sdk.postSessionIdPermissionsPermissionId({
-              path: { id: sessionID, permissionID: permission.id },
-              body: { response },
+            await sdk.permission.respond({
+              sessionID,
+              permissionID: permission.id,
+              response,
             })
           }
         }
@@ -222,23 +223,19 @@ export const RunCommand = cmd({
 
       if (args.command) {
         await sdk.session.command({
-          path: { id: sessionID },
-          body: {
-            agent: args.agent || "build",
-            model: args.model,
-            command: args.command,
-            arguments: message,
-          },
+          sessionID,
+          agent: args.agent || "build",
+          model: args.model,
+          command: args.command,
+          arguments: message,
         })
       } else {
         const modelParam = args.model ? Provider.parseModel(args.model) : undefined
         await sdk.session.prompt({
-          path: { id: sessionID },
-          body: {
-            agent: args.agent || "build",
-            model: modelParam,
-            parts: [...fileParts, { type: "text", text: message }],
-          },
+          sessionID,
+          agent: args.agent || "build",
+          model: modelParam,
+          parts: [...fileParts, { type: "text", text: message }],
         })
       }
 
@@ -263,7 +260,7 @@ export const RunCommand = cmd({
               : args.title
             : undefined
 
-        const result = await sdk.session.create({ body: title ? { title } : {} })
+        const result = await sdk.session.create(title ? { title } : {})
         return result.data?.id
       })()
 
@@ -274,7 +271,7 @@ export const RunCommand = cmd({
 
       const cfgResult = await sdk.config.get()
       if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
-        const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
+        const shareResult = await sdk.session.share({ sessionID }).catch((error) => {
           if (error instanceof Error && error.message.includes("disabled")) {
             UI.println(UI.Style.TEXT_DANGER_BOLD + "!  " + error.message)
           }
@@ -315,7 +312,7 @@ export const RunCommand = cmd({
               : args.title
             : undefined
 
-        const result = await sdk.session.create({ body: title ? { title } : {} })
+        const result = await sdk.session.create(title ? { title } : {})
         return result.data?.id
       })()
 
@@ -327,7 +324,7 @@ export const RunCommand = cmd({
 
       const cfgResult = await sdk.config.get()
       if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) {
-        const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => {
+        const shareResult = await sdk.session.share({ sessionID }).catch((error) => {
           if (error instanceof Error && error.message.includes("disabled")) {
             UI.println(UI.Style.TEXT_DANGER_BOLD + "!  " + error.message)
           }

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

@@ -11,7 +11,7 @@ import {
 } from "solid-js"
 import { useKeyboard } from "@opentui/solid"
 import { useKeybind } from "@tui/context/keybind"
-import type { KeybindsConfig } from "@opencode-ai/sdk"
+import type { KeybindsConfig } from "@opencode-ai/sdk/v2"
 
 type Context = ReturnType<typeof init>
 const ctx = createContext<Context>()

+ 10 - 24
packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx

@@ -7,7 +7,7 @@ import { useSDK } from "../context/sdk"
 import { DialogPrompt } from "../ui/dialog-prompt"
 import { useTheme } from "../context/theme"
 import { TextAttributes } from "@opentui/core"
-import type { ProviderAuthAuthorization } from "@opencode-ai/sdk"
+import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2"
 import { DialogModel } from "./dialog-model"
 
 const PROVIDER_PRIORITY: Record<string, number> = {
@@ -64,12 +64,8 @@ export function createDialogProviderOptions() {
           const method = methods[index]
           if (method.type === "oauth") {
             const result = await sdk.client.provider.oauth.authorize({
-              path: {
-                id: provider.id,
-              },
-              body: {
-                method: index,
-              },
+              providerID: provider.id,
+              method: index,
             })
             if (result.data?.method === "code") {
               dialog.replace(() => (
@@ -111,12 +107,8 @@ function AutoMethod(props: AutoMethodProps) {
 
   onMount(async () => {
     const result = await sdk.client.provider.oauth.callback({
-      path: {
-        id: props.providerID,
-      },
-      body: {
-        method: props.index,
-      },
+      providerID: props.providerID,
+      method: props.index,
     })
     if (result.error) {
       dialog.clear()
@@ -161,13 +153,9 @@ function CodeMethod(props: CodeMethodProps) {
       placeholder="Authorization code"
       onConfirm={async (value) => {
         const { error } = await sdk.client.provider.oauth.callback({
-          path: {
-            id: props.providerID,
-          },
-          body: {
-            method: props.index,
-            code: value,
-          },
+          providerID: props.providerID,
+          method: props.index,
+          code: value,
         })
         if (!error) {
           await sdk.client.instance.dispose()
@@ -219,10 +207,8 @@ function ApiMethod(props: ApiMethodProps) {
       onConfirm={async (value) => {
         if (!value) return
         sdk.client.auth.set({
-          path: {
-            id: props.providerID,
-          },
-          body: {
+          providerID: props.providerID,
+          auth: {
             type: "api",
             key: value,
           },

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

@@ -74,9 +74,7 @@ export function DialogSessionList() {
           onTrigger: async (option) => {
             if (toDelete() === option.value) {
               sdk.client.session.delete({
-                path: {
-                  id: option.value,
-                },
+                sessionID: option.value,
               })
               setToDelete(undefined)
               // dialog.clear()

+ 2 - 6
packages/opencode/src/cli/cmd/tui/component/dialog-session-rename.tsx

@@ -20,12 +20,8 @@ export function DialogSessionRename(props: DialogSessionRenameProps) {
       value={session()?.title}
       onConfirm={(value) => {
         sdk.client.session.update({
-          path: {
-            id: props.session,
-          },
-          body: {
-            title: value,
-          },
+          sessionID: props.session,
+          title: value,
         })
         dialog.clear()
       }}

+ 1 - 3
packages/opencode/src/cli/cmd/tui/component/dialog-tag.tsx

@@ -16,9 +16,7 @@ export function DialogTag(props: { onSelect?: (value: string) => void }) {
     () => [store.filter],
     async () => {
       const result = await sdk.client.find.files({
-        query: {
-          query: store.filter,
-        },
+        query: store.filter,
       })
       if (result.error) return []
       const sliced = (result.data ?? []).slice(0, 5)

+ 1 - 3
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

@@ -140,9 +140,7 @@ export function Autocomplete(props: {
 
       // Get files from SDK
       const result = await sdk.client.find.files({
-        query: {
-          query: query ?? "",
-        },
+        query: query ?? "",
       })
 
       const options: AutocompleteOption[] = []

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

@@ -5,7 +5,7 @@ import { createStore, produce } from "solid-js/store"
 import { clone } from "remeda"
 import { createSimpleContext } from "../../context/helper"
 import { appendFile, writeFile } from "fs/promises"
-import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk"
+import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk/v2"
 
 export type PromptInfo = {
   input: string

+ 30 - 44
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -17,7 +17,7 @@ import { useRenderer } from "@opentui/solid"
 import { Editor } from "@tui/util/editor"
 import { useExit } from "../../context/exit"
 import { Clipboard } from "../../util/clipboard"
-import type { FilePart } from "@opencode-ai/sdk"
+import type { FilePart } from "@opencode-ai/sdk/v2"
 import { TuiEvent } from "../../event"
 import { iife } from "@/util/iife"
 import { Locale } from "@/util/locale"
@@ -170,9 +170,7 @@ export function Prompt(props: PromptProps) {
 
           if (store.interrupt >= 2) {
             sdk.client.session.abort({
-              path: {
-                id: props.sessionID,
-              },
+              sessionID: props.sessionID,
             })
             setStore("interrupt", 0)
           }
@@ -447,17 +445,13 @@ export function Prompt(props: PromptProps) {
 
     if (store.mode === "shell") {
       sdk.client.session.shell({
-        path: {
-          id: sessionID,
-        },
-        body: {
-          agent: local.agent.current().name,
-          model: {
-            providerID: selectedModel.providerID,
-            modelID: selectedModel.modelID,
-          },
-          command: inputText,
+        sessionID,
+        agent: local.agent.current().name,
+        model: {
+          providerID: selectedModel.providerID,
+          modelID: selectedModel.modelID,
         },
+        command: inputText,
       })
       setStore("mode", "normal")
     } else if (
@@ -470,39 +464,31 @@ export function Prompt(props: PromptProps) {
     ) {
       let [command, ...args] = inputText.split(" ")
       sdk.client.session.command({
-        path: {
-          id: sessionID,
-        },
-        body: {
-          command: command.slice(1),
-          arguments: args.join(" "),
-          agent: local.agent.current().name,
-          model: `${selectedModel.providerID}/${selectedModel.modelID}`,
-          messageID,
-        },
+        sessionID,
+        command: command.slice(1),
+        arguments: args.join(" "),
+        agent: local.agent.current().name,
+        model: `${selectedModel.providerID}/${selectedModel.modelID}`,
+        messageID,
       })
     } else {
       sdk.client.session.prompt({
-        path: {
-          id: sessionID,
-        },
-        body: {
-          ...selectedModel,
-          messageID,
-          agent: local.agent.current().name,
-          model: selectedModel,
-          parts: [
-            {
-              id: Identifier.ascending("part"),
-              type: "text",
-              text: inputText,
-            },
-            ...nonTextParts.map((x) => ({
-              id: Identifier.ascending("part"),
-              ...x,
-            })),
-          ],
-        },
+        sessionID,
+        ...selectedModel,
+        messageID,
+        agent: local.agent.current().name,
+        model: selectedModel,
+        parts: [
+          {
+            id: Identifier.ascending("part"),
+            type: "text",
+            text: inputText,
+          },
+          ...nonTextParts.map((x) => ({
+            id: Identifier.ascending("part"),
+            ...x,
+          })),
+        ],
       })
     }
     history.append(store.prompt)

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

@@ -2,7 +2,7 @@ import { createMemo } from "solid-js"
 import { useSync } from "@tui/context/sync"
 import { Keybind } from "@/util/keybind"
 import { pipe, mapValues } from "remeda"
-import type { KeybindsConfig } from "@opencode-ai/sdk"
+import type { KeybindsConfig } from "@opencode-ai/sdk/v2"
 import type { ParsedKey, Renderable } from "@opentui/core"
 import { createStore } from "solid-js/store"
 import { useKeyboard, useRenderer } from "@opentui/solid"

+ 7 - 4
packages/opencode/src/cli/cmd/tui/context/sdk.tsx

@@ -1,4 +1,4 @@
-import { createOpencodeClient, type Event } from "@opencode-ai/sdk"
+import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2"
 import { createSimpleContext } from "./helper"
 import { createGlobalEmitter } from "@solid-primitives/event-bus"
 import { batch, onCleanup, onMount } from "solid-js"
@@ -20,9 +20,12 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
     onMount(async () => {
       while (true) {
         if (abort.signal.aborted) break
-        const events = await sdk.event.subscribe({
-          signal: abort.signal,
-        })
+        const events = await sdk.event.subscribe(
+          {},
+          {
+            signal: abort.signal,
+          },
+        )
         let queue: Event[] = []
         let timer: Timer | undefined
         let last = 0

+ 9 - 9
packages/opencode/src/cli/cmd/tui/context/sync.tsx

@@ -15,7 +15,7 @@ import type {
   ProviderListResponse,
   ProviderAuthMethod,
   VcsInfo,
-} from "@opencode-ai/sdk"
+} from "@opencode-ai/sdk/v2"
 import { createStore, produce, reconcile } from "solid-js/store"
 import { useSDK } from "@tui/context/sdk"
 import { Binary } from "@opencode-ai/util/binary"
@@ -255,19 +255,19 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
     async function bootstrap() {
       // blocking
       await Promise.all([
-        sdk.client.config.providers({ throwOnError: true }).then((x) => {
+        sdk.client.config.providers({}, { throwOnError: true }).then((x) => {
           batch(() => {
             setStore("provider", x.data!.providers)
             setStore("provider_default", x.data!.default)
           })
         }),
-        sdk.client.provider.list({ throwOnError: true }).then((x) => {
+        sdk.client.provider.list({}, { throwOnError: true }).then((x) => {
           batch(() => {
             setStore("provider_next", x.data!)
           })
         }),
-        sdk.client.app.agents({ throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
-        sdk.client.config.get({ throwOnError: true }).then((x) => setStore("config", x.data!)),
+        sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", x.data ?? [])),
+        sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", x.data!)),
       ])
         .then(() => {
           if (store.status !== "complete") setStore("status", "partial")
@@ -333,10 +333,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
         async sync(sessionID: string) {
           if (fullSyncedSessions.has(sessionID)) return
           const [session, messages, todo, diff] = await Promise.all([
-            sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }),
-            sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }),
-            sdk.client.session.todo({ path: { id: sessionID } }),
-            sdk.client.session.diff({ path: { id: sessionID } }),
+            sdk.client.session.get({ sessionID }, { throwOnError: true }),
+            sdk.client.session.messages({ sessionID, limit: 100 }),
+            sdk.client.session.todo({ sessionID }),
+            sdk.client.session.diff({ sessionID }),
           ])
           setStore(
             produce((draft) => {

+ 4 - 12
packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx

@@ -29,12 +29,8 @@ export function DialogMessage(props: {
             if (!msg) return
 
             sdk.client.session.revert({
-              path: {
-                id: props.sessionID,
-              },
-              body: {
-                messageID: msg.id,
-              },
+              sessionID: props.sessionID,
+              messageID: msg.id,
             })
 
             if (props.setPrompt) {
@@ -81,12 +77,8 @@ export function DialogMessage(props: {
           description: "create a new session",
           onSelect: async (dialog) => {
             const result = await sdk.client.session.fork({
-              path: {
-                id: props.sessionID,
-              },
-              body: {
-                messageID: props.messageID,
-              },
+              sessionID: props.sessionID,
+              messageID: props.messageID,
             })
             route.navigate({
               sessionID: result.data!.id,

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

@@ -1,7 +1,7 @@
 import { createMemo, onMount } from "solid-js"
 import { useSync } from "@tui/context/sync"
 import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
-import type { TextPart } from "@opencode-ai/sdk"
+import type { TextPart } from "@opencode-ai/sdk/v2"
 import { Locale } from "@/util/locale"
 import { DialogMessage } from "./dialog-message"
 import { useDialog } from "../../ui/dialog"

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

@@ -4,7 +4,7 @@ import { useSync } from "@tui/context/sync"
 import { pipe, sumBy } from "remeda"
 import { useTheme } from "@tui/context/theme"
 import { SplitBorder, EmptyBorder } from "@tui/component/border"
-import type { AssistantMessage, Session } from "@opencode-ai/sdk"
+import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2"
 import { useDirectory } from "../../context/directory"
 import { useKeybind } from "../../context/keybind"
 

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

@@ -25,7 +25,7 @@ import {
   type ScrollAcceleration,
 } from "@opentui/core"
 import { Prompt, type PromptRef } from "@tui/component/prompt"
-import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk"
+import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk/v2"
 import { useLocal } from "@tui/context/local"
 import { Locale } from "@/util/locale"
 import type { Tool } from "@/tool/tool"
@@ -150,7 +150,8 @@ export function Session() {
       .then(() => {
         if (scroll) scroll.scrollBy(100_000)
       })
-      .catch(() => {
+      .catch((e) => {
+        console.error(e)
         toast.show({
           message: `Session not found: ${route.sessionID}`,
           variant: "error",
@@ -202,14 +203,10 @@ export function Session() {
         return
       })
       if (response) {
-        sdk.client.postSessionIdPermissionsPermissionId({
-          path: {
-            permissionID: first.id,
-            id: route.sessionID,
-          },
-          body: {
-            response: response,
-          },
+        sdk.client.permission.respond({
+          permissionID: first.id,
+          sessionID: route.sessionID,
+          response: response,
         })
       }
     }
@@ -254,9 +251,7 @@ export function Session() {
             onSelect: async (dialog: any) => {
               await sdk.client.session
                 .share({
-                  path: {
-                    id: route.sessionID,
-                  },
+                  sessionID: route.sessionID,
                 })
                 .then((res) =>
                   Clipboard.copy(res.data!.share!.url).catch(() =>
@@ -314,13 +309,9 @@ export function Session() {
           return
         }
         sdk.client.session.summarize({
-          path: {
-            id: route.sessionID,
-          },
-          body: {
-            modelID: selectedModel.modelID,
-            providerID: selectedModel.providerID,
-          },
+          sessionID: route.sessionID,
+          modelID: selectedModel.modelID,
+          providerID: selectedModel.providerID,
         })
         dialog.clear()
       },
@@ -333,9 +324,7 @@ export function Session() {
       category: "Session",
       onSelect: (dialog) => {
         sdk.client.session.unshare({
-          path: {
-            id: route.sessionID,
-          },
+          sessionID: route.sessionID,
         })
         dialog.clear()
       },
@@ -347,18 +336,14 @@ export function Session() {
       category: "Session",
       onSelect: async (dialog) => {
         const status = sync.data.session_status[route.sessionID]
-        if (status?.type !== "idle") await sdk.client.session.abort({ path: { id: route.sessionID } }).catch(() => {})
+        if (status?.type !== "idle") await sdk.client.session.abort({ sessionID: route.sessionID }).catch(() => {})
         const revert = session().revert?.messageID
         const message = messages().findLast((x) => (!revert || x.id < revert) && x.role === "user")
         if (!message) return
         sdk.client.session
           .revert({
-            path: {
-              id: route.sessionID,
-            },
-            body: {
-              messageID: message.id,
-            },
+            sessionID: route.sessionID,
+            messageID: message.id,
           })
           .then(() => {
             toBottom()
@@ -392,20 +377,14 @@ export function Session() {
         const message = messages().find((x) => x.role === "user" && x.id > messageID)
         if (!message) {
           sdk.client.session.unrevert({
-            path: {
-              id: route.sessionID,
-            },
+            sessionID: route.sessionID,
           })
           prompt.set({ input: "", parts: [] })
           return
         }
         sdk.client.session.revert({
-          path: {
-            id: route.sessionID,
-          },
-          body: {
-            messageID: message.id,
-          },
+          sessionID: route.sessionID,
+          messageID: message.id,
         })
       },
     },
@@ -1066,7 +1045,7 @@ function UserMessage(props: {
               </box>
             </Show>
             <text fg={theme.textMuted}>
-              {ctx.usernameVisible() ? `${sync.data.config.username ?? "You"}` : "You"}
+              {ctx.usernameVisible() ? `${sync.data.config.username ?? "You "}` : "You "}
               <Show
                 when={queued()}
                 fallback={

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

@@ -4,7 +4,7 @@ import { createStore } from "solid-js/store"
 import { useTheme } from "../../context/theme"
 import { Locale } from "@/util/locale"
 import path from "path"
-import type { AssistantMessage } from "@opencode-ai/sdk"
+import type { AssistantMessage } from "@opencode-ai/sdk/v2"
 import { Global } from "@/global"
 import { Installation } from "@/installation"
 import { useKeybind } from "../../context/keybind"

+ 8 - 4
packages/opencode/src/plugin/index.ts

@@ -61,10 +61,14 @@ export namespace Plugin {
     for (const hook of await state().then((x) => x.hooks)) {
       const fn = hook[name]
       if (!fn) continue
-      // @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
-      // give up.
-      // try-counter: 2
-      await fn(input, output)
+      try {
+        // @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
+        // give up.
+        // try-counter: 2
+        await fn(input, output)
+      } catch (e) {
+        log.error("failed to trigger hook", { name, error: e })
+      }
     }
     return output
   }

+ 102 - 100
packages/opencode/src/server/server.ts

@@ -209,7 +209,7 @@ export namespace Server {
         },
       )
       .get(
-        "/pty/:id",
+        "/pty/:ptyID",
         describeRoute({
           description: "Get PTY session info",
           operationId: "pty.get",
@@ -225,9 +225,9 @@ export namespace Server {
             ...errors(404),
           },
         }),
-        validator("param", z.object({ id: z.string() })),
+        validator("param", z.object({ ptyID: z.string() })),
         async (c) => {
-          const info = Pty.get(c.req.valid("param").id)
+          const info = Pty.get(c.req.valid("param").ptyID)
           if (!info) {
             throw new Storage.NotFoundError({ message: "Session not found" })
           }
@@ -235,7 +235,7 @@ export namespace Server {
         },
       )
       .put(
-        "/pty/:id",
+        "/pty/:ptyID",
         describeRoute({
           description: "Update PTY session",
           operationId: "pty.update",
@@ -251,15 +251,15 @@ export namespace Server {
             ...errors(400),
           },
         }),
-        validator("param", z.object({ id: z.string() })),
+        validator("param", z.object({ ptyID: z.string() })),
         validator("json", Pty.UpdateInput),
         async (c) => {
-          const info = await Pty.update(c.req.valid("param").id, c.req.valid("json"))
+          const info = await Pty.update(c.req.valid("param").ptyID, c.req.valid("json"))
           return c.json(info)
         },
       )
       .delete(
-        "/pty/:id",
+        "/pty/:ptyID",
         describeRoute({
           description: "Remove a PTY session",
           operationId: "pty.remove",
@@ -275,14 +275,14 @@ export namespace Server {
             ...errors(404),
           },
         }),
-        validator("param", z.object({ id: z.string() })),
+        validator("param", z.object({ ptyID: z.string() })),
         async (c) => {
-          await Pty.remove(c.req.valid("param").id)
+          await Pty.remove(c.req.valid("param").ptyID)
           return c.json(true)
         },
       )
       .get(
-        "/pty/:id/connect",
+        "/pty/:ptyID/connect",
         describeRoute({
           description: "Connect to a PTY session",
           operationId: "pty.connect",
@@ -298,9 +298,9 @@ export namespace Server {
             ...errors(404),
           },
         }),
-        validator("param", z.object({ id: z.string() })),
+        validator("param", z.object({ ptyID: z.string() })),
         upgradeWebSocket((c) => {
-          const id = c.req.param("id")
+          const id = c.req.param("ptyID")
           let handler: ReturnType<typeof Pty.connect>
           if (!Pty.get(id)) throw new Error("Session not found")
           return {
@@ -557,7 +557,7 @@ export namespace Server {
         },
       )
       .get(
-        "/session/:id",
+        "/session/:sessionID",
         describeRoute({
           description: "Get session",
           operationId: "session.get",
@@ -576,17 +576,18 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: Session.get.schema,
+            sessionID: Session.get.schema,
           }),
         ),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
+          log.info("SEARCH", { url: c.req.url })
           const session = await Session.get(sessionID)
           return c.json(session)
         },
       )
       .get(
-        "/session/:id/children",
+        "/session/:sessionID/children",
         describeRoute({
           description: "Get a session's children",
           operationId: "session.children",
@@ -605,17 +606,17 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: Session.children.schema,
+            sessionID: Session.children.schema,
           }),
         ),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const session = await Session.children(sessionID)
           return c.json(session)
         },
       )
       .get(
-        "/session/:id/todo",
+        "/session/:sessionID/todo",
         describeRoute({
           description: "Get the todo list for a session",
           operationId: "session.todo",
@@ -634,11 +635,11 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const todos = await Todo.get(sessionID)
           return c.json(todos)
         },
@@ -668,7 +669,7 @@ export namespace Server {
         },
       )
       .delete(
-        "/session/:id",
+        "/session/:sessionID",
         describeRoute({
           description: "Delete a session and all its data",
           operationId: "session.delete",
@@ -687,11 +688,11 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: Session.remove.schema,
+            sessionID: Session.remove.schema,
           }),
         ),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           await Session.remove(sessionID)
           await Bus.publish(TuiEvent.CommandExecute, {
             command: "session.list",
@@ -700,7 +701,7 @@ export namespace Server {
         },
       )
       .patch(
-        "/session/:id",
+        "/session/:sessionID",
         describeRoute({
           description: "Update session properties",
           operationId: "session.update",
@@ -719,7 +720,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
           }),
         ),
         validator(
@@ -729,7 +730,7 @@ export namespace Server {
           }),
         ),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const updates = c.req.valid("json")
 
           const updatedSession = await Session.update(sessionID, (session) => {
@@ -742,7 +743,7 @@ export namespace Server {
         },
       )
       .post(
-        "/session/:id/init",
+        "/session/:sessionID/init",
         describeRoute({
           description: "Analyze the app and create an AGENTS.md file",
           operationId: "session.init",
@@ -761,19 +762,19 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator("json", Session.initialize.schema.omit({ sessionID: true })),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const body = c.req.valid("json")
           await Session.initialize({ ...body, sessionID })
           return c.json(true)
         },
       )
       .post(
-        "/session/:id/fork",
+        "/session/:sessionID/fork",
         describeRoute({
           description: "Fork an existing session at a specific message",
           operationId: "session.fork",
@@ -791,19 +792,19 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: Session.fork.schema.shape.sessionID,
+            sessionID: Session.fork.schema.shape.sessionID,
           }),
         ),
         validator("json", Session.fork.schema.omit({ sessionID: true })),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const body = c.req.valid("json")
           const result = await Session.fork({ ...body, sessionID })
           return c.json(result)
         },
       )
       .post(
-        "/session/:id/abort",
+        "/session/:sessionID/abort",
         describeRoute({
           description: "Abort a session",
           operationId: "session.abort",
@@ -822,16 +823,16 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
           }),
         ),
         async (c) => {
-          SessionPrompt.cancel(c.req.valid("param").id)
+          SessionPrompt.cancel(c.req.valid("param").sessionID)
           return c.json(true)
         },
       )
       .post(
-        "/session/:id/share",
+        "/session/:sessionID/share",
         describeRoute({
           description: "Share a session",
           operationId: "session.share",
@@ -850,18 +851,18 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
-          await Session.share(id)
-          const session = await Session.get(id)
+          const sessionID = c.req.valid("param").sessionID
+          await Session.share(sessionID)
+          const session = await Session.get(sessionID)
           return c.json(session)
         },
       )
       .get(
-        "/session/:id/diff",
+        "/session/:sessionID/diff",
         describeRoute({
           description: "Get the diff that resulted from this user message",
           operationId: "session.diff",
@@ -879,7 +880,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: SessionSummary.diff.schema.shape.sessionID,
+            sessionID: SessionSummary.diff.schema.shape.sessionID,
           }),
         ),
         validator(
@@ -892,14 +893,14 @@ export namespace Server {
           const query = c.req.valid("query")
           const params = c.req.valid("param")
           const result = await SessionSummary.diff({
-            sessionID: params.id,
+            sessionID: params.sessionID,
             messageID: query.messageID,
           })
           return c.json(result)
         },
       )
       .delete(
-        "/session/:id/share",
+        "/session/:sessionID/share",
         describeRoute({
           description: "Unshare the session",
           operationId: "session.unshare",
@@ -918,18 +919,18 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: Session.unshare.schema,
+            sessionID: Session.unshare.schema,
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
-          await Session.unshare(id)
-          const session = await Session.get(id)
+          const sessionID = c.req.valid("param").sessionID
+          await Session.unshare(sessionID)
+          const session = await Session.get(sessionID)
           return c.json(session)
         },
       )
       .post(
-        "/session/:id/summarize",
+        "/session/:sessionID/summarize",
         describeRoute({
           description: "Summarize the session",
           operationId: "session.summarize",
@@ -948,7 +949,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator(
@@ -959,9 +960,9 @@ export namespace Server {
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const body = c.req.valid("json")
-          const msgs = await Session.messages({ sessionID: id })
+          const msgs = await Session.messages({ sessionID })
           let currentAgent = "build"
           for (let i = msgs.length - 1; i >= 0; i--) {
             const info = msgs[i].info
@@ -971,7 +972,7 @@ export namespace Server {
             }
           }
           await SessionCompaction.create({
-            sessionID: id,
+            sessionID,
             agent: currentAgent,
             model: {
               providerID: body.providerID,
@@ -979,12 +980,12 @@ export namespace Server {
             },
             auto: false,
           })
-          await SessionPrompt.loop(id)
+          await SessionPrompt.loop(sessionID)
           return c.json(true)
         },
       )
       .get(
-        "/session/:id/message",
+        "/session/:sessionID/message",
         describeRoute({
           description: "List messages for a session",
           operationId: "session.messages",
@@ -1003,7 +1004,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator(
@@ -1015,14 +1016,14 @@ export namespace Server {
         async (c) => {
           const query = c.req.valid("query")
           const messages = await Session.messages({
-            sessionID: c.req.valid("param").id,
+            sessionID: c.req.valid("param").sessionID,
             limit: query.limit,
           })
           return c.json(messages)
         },
       )
       .get(
-        "/session/:id/diff",
+        "/session/:sessionID/diff",
         describeRoute({
           description: "Get the diff for this session",
           operationId: "session.diff",
@@ -1041,16 +1042,16 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         async (c) => {
-          const diff = await Session.diff(c.req.valid("param").id)
+          const diff = await Session.diff(c.req.valid("param").sessionID)
           return c.json(diff)
         },
       )
       .get(
-        "/session/:id/message/:messageID",
+        "/session/:sessionID/message/:messageID",
         describeRoute({
           description: "Get a message from a session",
           operationId: "session.message",
@@ -1074,21 +1075,21 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
             messageID: z.string().meta({ description: "Message ID" }),
           }),
         ),
         async (c) => {
           const params = c.req.valid("param")
           const message = await MessageV2.get({
-            sessionID: params.id,
+            sessionID: params.sessionID,
             messageID: params.messageID,
           })
           return c.json(message)
         },
       )
       .post(
-        "/session/:id/message",
+        "/session/:sessionID/message",
         describeRoute({
           description: "Create and send a new message to a session",
           operationId: "session.prompt",
@@ -1112,7 +1113,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
@@ -1120,7 +1121,7 @@ export namespace Server {
           c.status(200)
           c.header("Content-Type", "application/json")
           return stream(c, async (stream) => {
-            const sessionID = c.req.valid("param").id
+            const sessionID = c.req.valid("param").sessionID
             const body = c.req.valid("json")
             const msg = await SessionPrompt.prompt({ ...body, sessionID })
             stream.write(JSON.stringify(msg))
@@ -1128,7 +1129,7 @@ export namespace Server {
         },
       )
       .post(
-        "/session/:id/prompt_async",
+        "/session/:sessionID/prompt_async",
         describeRoute({
           description: "Create and send a new message to a session, start if needed and return immediately",
           operationId: "session.prompt_async",
@@ -1142,7 +1143,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
@@ -1150,14 +1151,14 @@ export namespace Server {
           c.status(204)
           c.header("Content-Type", "application/json")
           return stream(c, async () => {
-            const sessionID = c.req.valid("param").id
+            const sessionID = c.req.valid("param").sessionID
             const body = c.req.valid("json")
             SessionPrompt.prompt({ ...body, sessionID })
           })
         },
       )
       .post(
-        "/session/:id/command",
+        "/session/:sessionID/command",
         describeRoute({
           description: "Send a new command to a session",
           operationId: "session.command",
@@ -1181,19 +1182,19 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const body = c.req.valid("json")
           const msg = await SessionPrompt.command({ ...body, sessionID })
           return c.json(msg)
         },
       )
       .post(
-        "/session/:id/shell",
+        "/session/:sessionID/shell",
         describeRoute({
           description: "Run a shell command",
           operationId: "session.shell",
@@ -1212,19 +1213,19 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Session ID" }),
+            sessionID: z.string().meta({ description: "Session ID" }),
           }),
         ),
         validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
         async (c) => {
-          const sessionID = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           const body = c.req.valid("json")
           const msg = await SessionPrompt.shell({ ...body, sessionID })
           return c.json(msg)
         },
       )
       .post(
-        "/session/:id/revert",
+        "/session/:sessionID/revert",
         describeRoute({
           description: "Revert a message",
           operationId: "session.revert",
@@ -1243,22 +1244,22 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
           }),
         ),
         validator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
         async (c) => {
-          const id = c.req.valid("param").id
+          const sessionID = c.req.valid("param").sessionID
           log.info("revert", c.req.valid("json"))
           const session = await SessionRevert.revert({
-            sessionID: id,
+            sessionID,
             ...c.req.valid("json"),
           })
           return c.json(session)
         },
       )
       .post(
-        "/session/:id/unrevert",
+        "/session/:sessionID/unrevert",
         describeRoute({
           description: "Restore all reverted messages",
           operationId: "session.unrevert",
@@ -1277,19 +1278,20 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
-          const session = await SessionRevert.unrevert({ sessionID: id })
+          const sessionID = c.req.valid("param").sessionID
+          const session = await SessionRevert.unrevert({ sessionID })
           return c.json(session)
         },
       )
       .post(
-        "/session/:id/permissions/:permissionID",
+        "/session/:sessionID/permissions/:permissionID",
         describeRoute({
           description: "Respond to a permission request",
+          operationId: "permission.respond",
           responses: {
             200: {
               description: "Permission processed successfully",
@@ -1305,17 +1307,17 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            sessionID: z.string(),
             permissionID: z.string(),
           }),
         ),
         validator("json", z.object({ response: Permission.Response })),
         async (c) => {
           const params = c.req.valid("param")
-          const id = params.id
+          const sessionID = params.sessionID
           const permissionID = params.permissionID
           Permission.respond({
-            sessionID: id,
+            sessionID,
             permissionID,
             response: c.req.valid("json").response,
           })
@@ -1429,7 +1431,7 @@ export namespace Server {
         },
       )
       .post(
-        "/provider/:id/oauth/authorize",
+        "/provider/:providerID/oauth/authorize",
         describeRoute({
           description: "Authorize a provider using OAuth",
           operationId: "provider.oauth.authorize",
@@ -1448,7 +1450,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Provider ID" }),
+            providerID: z.string().meta({ description: "Provider ID" }),
           }),
         ),
         validator(
@@ -1458,17 +1460,17 @@ export namespace Server {
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
+          const providerID = c.req.valid("param").providerID
           const { method } = c.req.valid("json")
           const result = await ProviderAuth.authorize({
-            providerID: id,
+            providerID,
             method,
           })
           return c.json(result)
         },
       )
       .post(
-        "/provider/:id/oauth/callback",
+        "/provider/:providerID/oauth/callback",
         describeRoute({
           description: "Handle OAuth callback for a provider",
           operationId: "provider.oauth.callback",
@@ -1487,7 +1489,7 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string().meta({ description: "Provider ID" }),
+            providerID: z.string().meta({ description: "Provider ID" }),
           }),
         ),
         validator(
@@ -1498,10 +1500,10 @@ export namespace Server {
           }),
         ),
         async (c) => {
-          const id = c.req.valid("param").id
+          const providerID = c.req.valid("param").providerID
           const { method, code } = c.req.valid("json")
           await ProviderAuth.callback({
-            providerID: id,
+            providerID,
             method,
             code,
           })
@@ -2215,7 +2217,7 @@ export namespace Server {
       )
       .route("/tui/control", TuiRoute)
       .put(
-        "/auth/:id",
+        "/auth/:providerID",
         describeRoute({
           description: "Set authentication credentials",
           operationId: "auth.set",
@@ -2234,14 +2236,14 @@ export namespace Server {
         validator(
           "param",
           z.object({
-            id: z.string(),
+            providerID: z.string(),
           }),
         ),
         validator("json", Auth.Info),
         async (c) => {
-          const id = c.req.valid("param").id
+          const providerID = c.req.valid("param").providerID
           const info = c.req.valid("json")
-          await Auth.set(id, info)
+          await Auth.set(providerID, info)
           return c.json(true)
         },
       )

+ 1 - 1
packages/opencode/src/share/share-next.ts

@@ -6,7 +6,7 @@ import { Session } from "@/session"
 import { MessageV2 } from "@/session/message-v2"
 import { Storage } from "@/storage/storage"
 import { Log } from "@/util/log"
-import type * as SDK from "@opencode-ai/sdk"
+import type * as SDK from "@opencode-ai/sdk/v2"
 
 export namespace ShareNext {
   const log = Log.create({ service: "share-next" })

File diff ditekan karena terlalu besar
+ 252 - 100
packages/sdk/js/openapi.json


+ 5 - 2
packages/sdk/js/package.json

@@ -10,13 +10,16 @@
   "exports": {
     ".": "./src/index.ts",
     "./client": "./src/client.ts",
-    "./server": "./src/server.ts"
+    "./server": "./src/server.ts",
+    "./v2": "./src/v2/index.ts",
+    "./v2/client": "./src/v2/client.ts",
+    "./v2/server": "./src/v2/server.ts"
   },
   "files": [
     "dist"
   ],
   "devDependencies": {
-    "@hey-api/openapi-ts": "0.81.0",
+    "@hey-api/openapi-ts": "0.88.1",
     "@tsconfig/node22": "catalog:",
     "@types/node": "catalog:",
     "typescript": "catalog:",

+ 5 - 3
packages/sdk/js/script/build.ts

@@ -10,13 +10,12 @@ import { createClient } from "@hey-api/openapi-ts"
 
 await $`bun dev generate > ${dir}/openapi.json`.cwd(path.resolve(dir, "../../opencode"))
 
-await $`rm -rf src/gen`
-
 await createClient({
   input: "./openapi.json",
   output: {
-    path: "./src/gen",
+    path: "./src/v2/gen",
     tsConfigPath: path.join(dir, "tsconfig.json"),
+    clean: true,
   },
   plugins: [
     {
@@ -28,6 +27,7 @@ await createClient({
       instance: "OpencodeClient",
       exportFromIndex: false,
       auth: false,
+      paramsStructure: "flat",
     },
     {
       name: "@hey-api/client-fetch",
@@ -36,6 +36,8 @@ await createClient({
     },
   ],
 })
+
 await $`bun prettier --write src/gen`
+await $`bun prettier --write src/v2`
 await $`rm -rf dist`
 await $`bun tsc`

+ 7 - 6
packages/sdk/js/src/client.ts

@@ -1,19 +1,20 @@
 export * from "./gen/types.gen.js"
-export { type Config as OpencodeClientConfig, OpencodeClient }
 
 import { createClient } from "./gen/client/client.gen.js"
 import { type Config } from "./gen/client/types.gen.js"
 import { OpencodeClient } from "./gen/sdk.gen.js"
+export { type Config as OpencodeClientConfig, OpencodeClient }
 
 export function createOpencodeClient(config?: Config & { directory?: string }) {
   if (!config?.fetch) {
+    const customFetch: any = (req: any) => {
+      // @ts-ignore
+      req.timeout = false
+      return fetch(req)
+    }
     config = {
       ...config,
-      fetch: (req) => {
-        // @ts-ignore
-        req.timeout = false
-        return fetch(req)
-      },
+      fetch: customFetch,
     }
   }
 

+ 111 - 0
packages/sdk/js/src/gen/core/queryKeySerializer.gen.ts

@@ -0,0 +1,111 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+/**
+ * JSON-friendly union that mirrors what Pinia Colada can hash.
+ */
+export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }
+
+/**
+ * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
+ */
+export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
+  if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+    return undefined
+  }
+  if (typeof value === "bigint") {
+    return value.toString()
+  }
+  if (value instanceof Date) {
+    return value.toISOString()
+  }
+  return value
+}
+
+/**
+ * Safely stringifies a value and parses it back into a JsonValue.
+ */
+export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
+  try {
+    const json = JSON.stringify(input, queryKeyJsonReplacer)
+    if (json === undefined) {
+      return undefined
+    }
+    return JSON.parse(json) as JsonValue
+  } catch {
+    return undefined
+  }
+}
+
+/**
+ * Detects plain objects (including objects with a null prototype).
+ */
+const isPlainObject = (value: unknown): value is Record<string, unknown> => {
+  if (value === null || typeof value !== "object") {
+    return false
+  }
+  const prototype = Object.getPrototypeOf(value as object)
+  return prototype === Object.prototype || prototype === null
+}
+
+/**
+ * Turns URLSearchParams into a sorted JSON object for deterministic keys.
+ */
+const serializeSearchParams = (params: URLSearchParams): JsonValue => {
+  const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b))
+  const result: Record<string, JsonValue> = {}
+
+  for (const [key, value] of entries) {
+    const existing = result[key]
+    if (existing === undefined) {
+      result[key] = value
+      continue
+    }
+
+    if (Array.isArray(existing)) {
+      ;(existing as string[]).push(value)
+    } else {
+      result[key] = [existing, value]
+    }
+  }
+
+  return result
+}
+
+/**
+ * Normalizes any accepted value into a JSON-friendly shape for query keys.
+ */
+export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
+  if (value === null) {
+    return null
+  }
+
+  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+    return value
+  }
+
+  if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+    return undefined
+  }
+
+  if (typeof value === "bigint") {
+    return value.toString()
+  }
+
+  if (value instanceof Date) {
+    return value.toISOString()
+  }
+
+  if (Array.isArray(value)) {
+    return stringifyToJsonValue(value)
+  }
+
+  if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
+    return serializeSearchParams(value)
+  }
+
+  if (isPlainObject(value)) {
+    return stringifyToJsonValue(value)
+  }
+
+  return undefined
+}

+ 30 - 0
packages/sdk/js/src/v2/client.ts

@@ -0,0 +1,30 @@
+export * from "./gen/types.gen.js"
+
+import { createClient } from "./gen/client/client.gen.js"
+import { type Config } from "./gen/client/types.gen.js"
+import { OpencodeClient } from "./gen/sdk.gen.js"
+export { type Config as OpencodeClientConfig, OpencodeClient }
+
+export function createOpencodeClient(config?: Config & { directory?: string }) {
+  if (!config?.fetch) {
+    const customFetch: any = (req: any) => {
+      // @ts-ignore
+      req.timeout = false
+      return fetch(req)
+    }
+    config = {
+      ...config,
+      fetch: customFetch,
+    }
+  }
+
+  if (config?.directory) {
+    config.headers = {
+      ...config.headers,
+      "x-opencode-directory": config.directory,
+    }
+  }
+
+  const client = createClient(config)
+  return new OpencodeClient({ client })
+}

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

@@ -0,0 +1,18 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { type ClientOptions, type Config, createClient, createConfig } from "./client/index.js"
+import type { ClientOptions as ClientOptions2 } from "./types.gen.js"
+
+/**
+ * The `createClientConfig()` function will be called on client initialization
+ * and the returned object will become the client's initial configuration.
+ *
+ * You may want to initialize your client this way instead of calling
+ * `setConfig()`. This is useful for example if you're using Next.js
+ * to ensure your client always has the correct values.
+ */
+export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
+  override?: Config<ClientOptions & T>,
+) => Config<Required<ClientOptions> & T>
+
+export const client = createClient(createConfig<ClientOptions2>({ baseUrl: "http://localhost:4096" }))

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

@@ -0,0 +1,278 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { createSseClient } from "../core/serverSentEvents.gen.js"
+import type { HttpMethod } from "../core/types.gen.js"
+import { getValidRequestBody } from "../core/utils.gen.js"
+import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen.js"
+import {
+  buildUrl,
+  createConfig,
+  createInterceptors,
+  getParseAs,
+  mergeConfigs,
+  mergeHeaders,
+  setAuthParams,
+} from "./utils.gen.js"
+
+type ReqInit = Omit<RequestInit, "body" | "headers"> & {
+  body?: any
+  headers: ReturnType<typeof mergeHeaders>
+}
+
+export const createClient = (config: Config = {}): Client => {
+  let _config = mergeConfigs(createConfig(), config)
+
+  const getConfig = (): Config => ({ ..._config })
+
+  const setConfig = (config: Config): Config => {
+    _config = mergeConfigs(_config, config)
+    return getConfig()
+  }
+
+  const interceptors = createInterceptors<Request, Response, unknown, ResolvedRequestOptions>()
+
+  const beforeRequest = async (options: RequestOptions) => {
+    const opts = {
+      ..._config,
+      ...options,
+      fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
+      headers: mergeHeaders(_config.headers, options.headers),
+      serializedBody: undefined,
+    }
+
+    if (opts.security) {
+      await setAuthParams({
+        ...opts,
+        security: opts.security,
+      })
+    }
+
+    if (opts.requestValidator) {
+      await opts.requestValidator(opts)
+    }
+
+    if (opts.body !== undefined && opts.bodySerializer) {
+      opts.serializedBody = opts.bodySerializer(opts.body)
+    }
+
+    // remove Content-Type header if body is empty to avoid sending invalid requests
+    if (opts.body === undefined || opts.serializedBody === "") {
+      opts.headers.delete("Content-Type")
+    }
+
+    const url = buildUrl(opts)
+
+    return { opts, url }
+  }
+
+  const request: Client["request"] = async (options) => {
+    // @ts-expect-error
+    const { opts, url } = await beforeRequest(options)
+    const requestInit: ReqInit = {
+      redirect: "follow",
+      ...opts,
+      body: getValidRequestBody(opts),
+    }
+
+    let request = new Request(url, requestInit)
+
+    for (const fn of interceptors.request.fns) {
+      if (fn) {
+        request = await fn(request, opts)
+      }
+    }
+
+    // fetch must be assigned here, otherwise it would throw the error:
+    // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
+    const _fetch = opts.fetch!
+    let response: Response
+
+    try {
+      response = await _fetch(request)
+    } catch (error) {
+      // Handle fetch exceptions (AbortError, network errors, etc.)
+      let finalError = error
+
+      for (const fn of interceptors.error.fns) {
+        if (fn) {
+          finalError = (await fn(error, undefined as any, request, opts)) as unknown
+        }
+      }
+
+      finalError = finalError || ({} as unknown)
+
+      if (opts.throwOnError) {
+        throw finalError
+      }
+
+      // Return error response
+      return opts.responseStyle === "data"
+        ? undefined
+        : {
+            error: finalError,
+            request,
+            response: undefined as any,
+          }
+    }
+
+    for (const fn of interceptors.response.fns) {
+      if (fn) {
+        response = await fn(response, request, opts)
+      }
+    }
+
+    const result = {
+      request,
+      response,
+    }
+
+    if (response.ok) {
+      const parseAs =
+        (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"
+
+      if (response.status === 204 || response.headers.get("Content-Length") === "0") {
+        let emptyData: any
+        switch (parseAs) {
+          case "arrayBuffer":
+          case "blob":
+          case "text":
+            emptyData = await response[parseAs]()
+            break
+          case "formData":
+            emptyData = new FormData()
+            break
+          case "stream":
+            emptyData = response.body
+            break
+          case "json":
+          default:
+            emptyData = {}
+            break
+        }
+        return opts.responseStyle === "data"
+          ? emptyData
+          : {
+              data: emptyData,
+              ...result,
+            }
+      }
+
+      let data: any
+      switch (parseAs) {
+        case "arrayBuffer":
+        case "blob":
+        case "formData":
+        case "json":
+        case "text":
+          data = await response[parseAs]()
+          break
+        case "stream":
+          return opts.responseStyle === "data"
+            ? response.body
+            : {
+                data: response.body,
+                ...result,
+              }
+      }
+
+      if (parseAs === "json") {
+        if (opts.responseValidator) {
+          await opts.responseValidator(data)
+        }
+
+        if (opts.responseTransformer) {
+          data = await opts.responseTransformer(data)
+        }
+      }
+
+      return opts.responseStyle === "data"
+        ? data
+        : {
+            data,
+            ...result,
+          }
+    }
+
+    const textError = await response.text()
+    let jsonError: unknown
+
+    try {
+      jsonError = JSON.parse(textError)
+    } catch {
+      // noop
+    }
+
+    const error = jsonError ?? textError
+    let finalError = error
+
+    for (const fn of interceptors.error.fns) {
+      if (fn) {
+        finalError = (await fn(error, response, request, opts)) as string
+      }
+    }
+
+    finalError = finalError || ({} as string)
+
+    if (opts.throwOnError) {
+      throw finalError
+    }
+
+    // TODO: we probably want to return error and improve types
+    return opts.responseStyle === "data"
+      ? undefined
+      : {
+          error: finalError,
+          ...result,
+        }
+  }
+
+  const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) => request({ ...options, method })
+
+  const makeSseFn = (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
+    const { opts, url } = await beforeRequest(options)
+    return createSseClient({
+      ...opts,
+      body: opts.body as BodyInit | null | undefined,
+      headers: opts.headers as unknown as Record<string, string>,
+      method,
+      onRequest: async (url, init) => {
+        let request = new Request(url, init)
+        for (const fn of interceptors.request.fns) {
+          if (fn) {
+            request = await fn(request, opts)
+          }
+        }
+        return request
+      },
+      url,
+    })
+  }
+
+  return {
+    buildUrl,
+    connect: makeMethodFn("CONNECT"),
+    delete: makeMethodFn("DELETE"),
+    get: makeMethodFn("GET"),
+    getConfig,
+    head: makeMethodFn("HEAD"),
+    interceptors,
+    options: makeMethodFn("OPTIONS"),
+    patch: makeMethodFn("PATCH"),
+    post: makeMethodFn("POST"),
+    put: makeMethodFn("PUT"),
+    request,
+    setConfig,
+    sse: {
+      connect: makeSseFn("CONNECT"),
+      delete: makeSseFn("DELETE"),
+      get: makeSseFn("GET"),
+      head: makeSseFn("HEAD"),
+      options: makeSseFn("OPTIONS"),
+      patch: makeSseFn("PATCH"),
+      post: makeSseFn("POST"),
+      put: makeSseFn("PUT"),
+      trace: makeSseFn("TRACE"),
+    },
+    trace: makeMethodFn("TRACE"),
+  } as Client
+}

+ 25 - 0
packages/sdk/js/src/v2/gen/client/index.ts

@@ -0,0 +1,25 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type { Auth } from "../core/auth.gen.js"
+export type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
+export {
+  formDataBodySerializer,
+  jsonBodySerializer,
+  urlSearchParamsBodySerializer,
+} from "../core/bodySerializer.gen.js"
+export { buildClientParams } from "../core/params.gen.js"
+export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen.js"
+export { createClient } from "./client.gen.js"
+export type {
+  Client,
+  ClientOptions,
+  Config,
+  CreateClientConfig,
+  Options,
+  RequestOptions,
+  RequestResult,
+  ResolvedRequestOptions,
+  ResponseStyle,
+  TDataShape,
+} from "./types.gen.js"
+export { createConfig, mergeHeaders } from "./utils.gen.js"

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

@@ -0,0 +1,202 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Auth } from "../core/auth.gen.js"
+import type { ServerSentEventsOptions, ServerSentEventsResult } from "../core/serverSentEvents.gen.js"
+import type { Client as CoreClient, Config as CoreConfig } from "../core/types.gen.js"
+import type { Middleware } from "./utils.gen.js"
+
+export type ResponseStyle = "data" | "fields"
+
+export interface Config<T extends ClientOptions = ClientOptions>
+  extends Omit<RequestInit, "body" | "headers" | "method">,
+    CoreConfig {
+  /**
+   * Base URL for all requests made by this client.
+   */
+  baseUrl?: T["baseUrl"]
+  /**
+   * Fetch API implementation. You can use this option to provide a custom
+   * fetch instance.
+   *
+   * @default globalThis.fetch
+   */
+  fetch?: typeof fetch
+  /**
+   * Please don't use the Fetch client for Next.js applications. The `next`
+   * options won't have any effect.
+   *
+   * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead.
+   */
+  next?: never
+  /**
+   * Return the response data parsed in a specified format. By default, `auto`
+   * will infer the appropriate method from the `Content-Type` response header.
+   * You can override this behavior with any of the {@link Body} methods.
+   * Select `stream` if you don't want to parse response data at all.
+   *
+   * @default 'auto'
+   */
+  parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text"
+  /**
+   * Should we return only data or multiple fields (data, error, response, etc.)?
+   *
+   * @default 'fields'
+   */
+  responseStyle?: ResponseStyle
+  /**
+   * Throw an error instead of returning it in the response?
+   *
+   * @default false
+   */
+  throwOnError?: T["throwOnError"]
+}
+
+export interface RequestOptions<
+  TData = unknown,
+  TResponseStyle extends ResponseStyle = "fields",
+  ThrowOnError extends boolean = boolean,
+  Url extends string = string,
+> extends Config<{
+      responseStyle: TResponseStyle
+      throwOnError: ThrowOnError
+    }>,
+    Pick<
+      ServerSentEventsOptions<TData>,
+      "onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay"
+    > {
+  /**
+   * Any body that you want to add to your request.
+   *
+   * {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
+   */
+  body?: unknown
+  path?: Record<string, unknown>
+  query?: Record<string, unknown>
+  /**
+   * Security mechanism(s) to use for the request.
+   */
+  security?: ReadonlyArray<Auth>
+  url: Url
+}
+
+export interface ResolvedRequestOptions<
+  TResponseStyle extends ResponseStyle = "fields",
+  ThrowOnError extends boolean = boolean,
+  Url extends string = string,
+> extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> {
+  serializedBody?: string
+}
+
+export type RequestResult<
+  TData = unknown,
+  TError = unknown,
+  ThrowOnError extends boolean = boolean,
+  TResponseStyle extends ResponseStyle = "fields",
+> = ThrowOnError extends true
+  ? Promise<
+      TResponseStyle extends "data"
+        ? TData extends Record<string, unknown>
+          ? TData[keyof TData]
+          : TData
+        : {
+            data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
+            request: Request
+            response: Response
+          }
+    >
+  : Promise<
+      TResponseStyle extends "data"
+        ? (TData extends Record<string, unknown> ? TData[keyof TData] : TData) | undefined
+        : (
+            | {
+                data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
+                error: undefined
+              }
+            | {
+                data: undefined
+                error: TError extends Record<string, unknown> ? TError[keyof TError] : TError
+              }
+          ) & {
+            request: Request
+            response: Response
+          }
+    >
+
+export interface ClientOptions {
+  baseUrl?: string
+  responseStyle?: ResponseStyle
+  throwOnError?: boolean
+}
+
+type MethodFn = <
+  TData = unknown,
+  TError = unknown,
+  ThrowOnError extends boolean = false,
+  TResponseStyle extends ResponseStyle = "fields",
+>(
+  options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
+) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
+
+type SseFn = <
+  TData = unknown,
+  TError = unknown,
+  ThrowOnError extends boolean = false,
+  TResponseStyle extends ResponseStyle = "fields",
+>(
+  options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
+) => Promise<ServerSentEventsResult<TData, TError>>
+
+type RequestFn = <
+  TData = unknown,
+  TError = unknown,
+  ThrowOnError extends boolean = false,
+  TResponseStyle extends ResponseStyle = "fields",
+>(
+  options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method"> &
+    Pick<Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, "method">,
+) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
+
+type BuildUrlFn = <
+  TData extends {
+    body?: unknown
+    path?: Record<string, unknown>
+    query?: Record<string, unknown>
+    url: string
+  },
+>(
+  options: TData & Options<TData>,
+) => string
+
+export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn> & {
+  interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions>
+}
+
+/**
+ * The `createClientConfig()` function will be called on client initialization
+ * and the returned object will become the client's initial configuration.
+ *
+ * You may want to initialize your client this way instead of calling
+ * `setConfig()`. This is useful for example if you're using Next.js
+ * to ensure your client always has the correct values.
+ */
+export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
+  override?: Config<ClientOptions & T>,
+) => Config<Required<ClientOptions> & T>
+
+export interface TDataShape {
+  body?: unknown
+  headers?: unknown
+  path?: unknown
+  query?: unknown
+  url: string
+}
+
+type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>
+
+export type Options<
+  TData extends TDataShape = TDataShape,
+  ThrowOnError extends boolean = boolean,
+  TResponse = unknown,
+  TResponseStyle extends ResponseStyle = "fields",
+> = OmitKeys<RequestOptions<TResponse, TResponseStyle, ThrowOnError>, "body" | "path" | "query" | "url"> &
+  ([TData] extends [never] ? unknown : Omit<TData, "url">)

+ 289 - 0
packages/sdk/js/src/v2/gen/client/utils.gen.ts

@@ -0,0 +1,289 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { getAuthToken } from "../core/auth.gen.js"
+import type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
+import { jsonBodySerializer } from "../core/bodySerializer.gen.js"
+import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.gen.js"
+import { getUrl } from "../core/utils.gen.js"
+import type { Client, ClientOptions, Config, RequestOptions } from "./types.gen.js"
+
+export const createQuerySerializer = <T = unknown>({ parameters = {}, ...args }: QuerySerializerOptions = {}) => {
+  const querySerializer = (queryParams: T) => {
+    const search: string[] = []
+    if (queryParams && typeof queryParams === "object") {
+      for (const name in queryParams) {
+        const value = queryParams[name]
+
+        if (value === undefined || value === null) {
+          continue
+        }
+
+        const options = parameters[name] || args
+
+        if (Array.isArray(value)) {
+          const serializedArray = serializeArrayParam({
+            allowReserved: options.allowReserved,
+            explode: true,
+            name,
+            style: "form",
+            value,
+            ...options.array,
+          })
+          if (serializedArray) search.push(serializedArray)
+        } else if (typeof value === "object") {
+          const serializedObject = serializeObjectParam({
+            allowReserved: options.allowReserved,
+            explode: true,
+            name,
+            style: "deepObject",
+            value: value as Record<string, unknown>,
+            ...options.object,
+          })
+          if (serializedObject) search.push(serializedObject)
+        } else {
+          const serializedPrimitive = serializePrimitiveParam({
+            allowReserved: options.allowReserved,
+            name,
+            value: value as string,
+          })
+          if (serializedPrimitive) search.push(serializedPrimitive)
+        }
+      }
+    }
+    return search.join("&")
+  }
+  return querySerializer
+}
+
+/**
+ * Infers parseAs value from provided Content-Type header.
+ */
+export const getParseAs = (contentType: string | null): Exclude<Config["parseAs"], "auto"> => {
+  if (!contentType) {
+    // If no Content-Type header is provided, the best we can do is return the raw response body,
+    // which is effectively the same as the 'stream' option.
+    return "stream"
+  }
+
+  const cleanContent = contentType.split(";")[0]?.trim()
+
+  if (!cleanContent) {
+    return
+  }
+
+  if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
+    return "json"
+  }
+
+  if (cleanContent === "multipart/form-data") {
+    return "formData"
+  }
+
+  if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
+    return "blob"
+  }
+
+  if (cleanContent.startsWith("text/")) {
+    return "text"
+  }
+
+  return
+}
+
+const checkForExistence = (
+  options: Pick<RequestOptions, "auth" | "query"> & {
+    headers: Headers
+  },
+  name?: string,
+): boolean => {
+  if (!name) {
+    return false
+  }
+  if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
+    return true
+  }
+  return false
+}
+
+export const setAuthParams = async ({
+  security,
+  ...options
+}: Pick<Required<RequestOptions>, "security"> &
+  Pick<RequestOptions, "auth" | "query"> & {
+    headers: Headers
+  }) => {
+  for (const auth of security) {
+    if (checkForExistence(options, auth.name)) {
+      continue
+    }
+
+    const token = await getAuthToken(auth, options.auth)
+
+    if (!token) {
+      continue
+    }
+
+    const name = auth.name ?? "Authorization"
+
+    switch (auth.in) {
+      case "query":
+        if (!options.query) {
+          options.query = {}
+        }
+        options.query[name] = token
+        break
+      case "cookie":
+        options.headers.append("Cookie", `${name}=${token}`)
+        break
+      case "header":
+      default:
+        options.headers.set(name, token)
+        break
+    }
+  }
+}
+
+export const buildUrl: Client["buildUrl"] = (options) =>
+  getUrl({
+    baseUrl: options.baseUrl as string,
+    path: options.path,
+    query: options.query,
+    querySerializer:
+      typeof options.querySerializer === "function"
+        ? options.querySerializer
+        : createQuerySerializer(options.querySerializer),
+    url: options.url,
+  })
+
+export const mergeConfigs = (a: Config, b: Config): Config => {
+  const config = { ...a, ...b }
+  if (config.baseUrl?.endsWith("/")) {
+    config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1)
+  }
+  config.headers = mergeHeaders(a.headers, b.headers)
+  return config
+}
+
+const headersEntries = (headers: Headers): Array<[string, string]> => {
+  const entries: Array<[string, string]> = []
+  headers.forEach((value, key) => {
+    entries.push([key, value])
+  })
+  return entries
+}
+
+export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | undefined>): Headers => {
+  const mergedHeaders = new Headers()
+  for (const header of headers) {
+    if (!header) {
+      continue
+    }
+
+    const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header)
+
+    for (const [key, value] of iterator) {
+      if (value === null) {
+        mergedHeaders.delete(key)
+      } else if (Array.isArray(value)) {
+        for (const v of value) {
+          mergedHeaders.append(key, v as string)
+        }
+      } else if (value !== undefined) {
+        // assume object headers are meant to be JSON stringified, i.e. their
+        // content value in OpenAPI specification is 'application/json'
+        mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string))
+      }
+    }
+  }
+  return mergedHeaders
+}
+
+type ErrInterceptor<Err, Res, Req, Options> = (
+  error: Err,
+  response: Res,
+  request: Req,
+  options: Options,
+) => Err | Promise<Err>
+
+type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>
+
+type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>
+
+class Interceptors<Interceptor> {
+  fns: Array<Interceptor | null> = []
+
+  clear(): void {
+    this.fns = []
+  }
+
+  eject(id: number | Interceptor): void {
+    const index = this.getInterceptorIndex(id)
+    if (this.fns[index]) {
+      this.fns[index] = null
+    }
+  }
+
+  exists(id: number | Interceptor): boolean {
+    const index = this.getInterceptorIndex(id)
+    return Boolean(this.fns[index])
+  }
+
+  getInterceptorIndex(id: number | Interceptor): number {
+    if (typeof id === "number") {
+      return this.fns[id] ? id : -1
+    }
+    return this.fns.indexOf(id)
+  }
+
+  update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false {
+    const index = this.getInterceptorIndex(id)
+    if (this.fns[index]) {
+      this.fns[index] = fn
+      return id
+    }
+    return false
+  }
+
+  use(fn: Interceptor): number {
+    this.fns.push(fn)
+    return this.fns.length - 1
+  }
+}
+
+export interface Middleware<Req, Res, Err, Options> {
+  error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>
+  request: Interceptors<ReqInterceptor<Req, Options>>
+  response: Interceptors<ResInterceptor<Res, Req, Options>>
+}
+
+export const createInterceptors = <Req, Res, Err, Options>(): Middleware<Req, Res, Err, Options> => ({
+  error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
+  request: new Interceptors<ReqInterceptor<Req, Options>>(),
+  response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
+})
+
+const defaultQuerySerializer = createQuerySerializer({
+  allowReserved: false,
+  array: {
+    explode: true,
+    style: "form",
+  },
+  object: {
+    explode: true,
+    style: "deepObject",
+  },
+})
+
+const defaultHeaders = {
+  "Content-Type": "application/json",
+}
+
+export const createConfig = <T extends ClientOptions = ClientOptions>(
+  override: Config<Omit<ClientOptions, keyof T> & T> = {},
+): Config<Omit<ClientOptions, keyof T> & T> => ({
+  ...jsonBodySerializer,
+  headers: defaultHeaders,
+  parseAs: "auto",
+  querySerializer: defaultQuerySerializer,
+  ...override,
+})

+ 41 - 0
packages/sdk/js/src/v2/gen/core/auth.gen.ts

@@ -0,0 +1,41 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type AuthToken = string | undefined
+
+export interface Auth {
+  /**
+   * Which part of the request do we use to send the auth?
+   *
+   * @default 'header'
+   */
+  in?: "header" | "query" | "cookie"
+  /**
+   * Header or query parameter name.
+   *
+   * @default 'Authorization'
+   */
+  name?: string
+  scheme?: "basic" | "bearer"
+  type: "apiKey" | "http"
+}
+
+export const getAuthToken = async (
+  auth: Auth,
+  callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
+): Promise<string | undefined> => {
+  const token = typeof callback === "function" ? await callback(auth) : callback
+
+  if (!token) {
+    return
+  }
+
+  if (auth.scheme === "bearer") {
+    return `Bearer ${token}`
+  }
+
+  if (auth.scheme === "basic") {
+    return `Basic ${btoa(token)}`
+  }
+
+  return token
+}

+ 82 - 0
packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts

@@ -0,0 +1,82 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.gen.js"
+
+export type QuerySerializer = (query: Record<string, unknown>) => string
+
+export type BodySerializer = (body: any) => any
+
+type QuerySerializerOptionsObject = {
+  allowReserved?: boolean
+  array?: Partial<SerializerOptions<ArrayStyle>>
+  object?: Partial<SerializerOptions<ObjectStyle>>
+}
+
+export type QuerySerializerOptions = QuerySerializerOptionsObject & {
+  /**
+   * Per-parameter serialization overrides. When provided, these settings
+   * override the global array/object settings for specific parameter names.
+   */
+  parameters?: Record<string, QuerySerializerOptionsObject>
+}
+
+const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => {
+  if (typeof value === "string" || value instanceof Blob) {
+    data.append(key, value)
+  } else if (value instanceof Date) {
+    data.append(key, value.toISOString())
+  } else {
+    data.append(key, JSON.stringify(value))
+  }
+}
+
+const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => {
+  if (typeof value === "string") {
+    data.append(key, value)
+  } else {
+    data.append(key, JSON.stringify(value))
+  }
+}
+
+export const formDataBodySerializer = {
+  bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
+    const data = new FormData()
+
+    Object.entries(body).forEach(([key, value]) => {
+      if (value === undefined || value === null) {
+        return
+      }
+      if (Array.isArray(value)) {
+        value.forEach((v) => serializeFormDataPair(data, key, v))
+      } else {
+        serializeFormDataPair(data, key, value)
+      }
+    })
+
+    return data
+  },
+}
+
+export const jsonBodySerializer = {
+  bodySerializer: <T>(body: T): string =>
+    JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)),
+}
+
+export const urlSearchParamsBodySerializer = {
+  bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): string => {
+    const data = new URLSearchParams()
+
+    Object.entries(body).forEach(([key, value]) => {
+      if (value === undefined || value === null) {
+        return
+      }
+      if (Array.isArray(value)) {
+        value.forEach((v) => serializeUrlSearchParamsPair(data, key, v))
+      } else {
+        serializeUrlSearchParamsPair(data, key, value)
+      }
+    })
+
+    return data.toString()
+  },
+}

+ 169 - 0
packages/sdk/js/src/v2/gen/core/params.gen.ts

@@ -0,0 +1,169 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+type Slot = "body" | "headers" | "path" | "query"
+
+export type Field =
+  | {
+      in: Exclude<Slot, "body">
+      /**
+       * Field name. This is the name we want the user to see and use.
+       */
+      key: string
+      /**
+       * Field mapped name. This is the name we want to use in the request.
+       * If omitted, we use the same value as `key`.
+       */
+      map?: string
+    }
+  | {
+      in: Extract<Slot, "body">
+      /**
+       * Key isn't required for bodies.
+       */
+      key?: string
+      map?: string
+    }
+  | {
+      /**
+       * Field name. This is the name we want the user to see and use.
+       */
+      key: string
+      /**
+       * Field mapped name. This is the name we want to use in the request.
+       * If `in` is omitted, `map` aliases `key` to the transport layer.
+       */
+      map: Slot
+    }
+
+export interface Fields {
+  allowExtra?: Partial<Record<Slot, boolean>>
+  args?: ReadonlyArray<Field>
+}
+
+export type FieldsConfig = ReadonlyArray<Field | Fields>
+
+const extraPrefixesMap: Record<string, Slot> = {
+  $body_: "body",
+  $headers_: "headers",
+  $path_: "path",
+  $query_: "query",
+}
+const extraPrefixes = Object.entries(extraPrefixesMap)
+
+type KeyMap = Map<
+  string,
+  | {
+      in: Slot
+      map?: string
+    }
+  | {
+      in?: never
+      map: Slot
+    }
+>
+
+const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
+  if (!map) {
+    map = new Map()
+  }
+
+  for (const config of fields) {
+    if ("in" in config) {
+      if (config.key) {
+        map.set(config.key, {
+          in: config.in,
+          map: config.map,
+        })
+      }
+    } else if ("key" in config) {
+      map.set(config.key, {
+        map: config.map,
+      })
+    } else if (config.args) {
+      buildKeyMap(config.args, map)
+    }
+  }
+
+  return map
+}
+
+interface Params {
+  body: unknown
+  headers: Record<string, unknown>
+  path: Record<string, unknown>
+  query: Record<string, unknown>
+}
+
+const stripEmptySlots = (params: Params) => {
+  for (const [slot, value] of Object.entries(params)) {
+    if (value && typeof value === "object" && !Object.keys(value).length) {
+      delete params[slot as Slot]
+    }
+  }
+}
+
+export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
+  const params: Params = {
+    body: {},
+    headers: {},
+    path: {},
+    query: {},
+  }
+
+  const map = buildKeyMap(fields)
+
+  let config: FieldsConfig[number] | undefined
+
+  for (const [index, arg] of args.entries()) {
+    if (fields[index]) {
+      config = fields[index]
+    }
+
+    if (!config) {
+      continue
+    }
+
+    if ("in" in config) {
+      if (config.key) {
+        const field = map.get(config.key)!
+        const name = field.map || config.key
+        if (field.in) {
+          ;(params[field.in] as Record<string, unknown>)[name] = arg
+        }
+      } else {
+        params.body = arg
+      }
+    } else {
+      for (const [key, value] of Object.entries(arg ?? {})) {
+        const field = map.get(key)
+
+        if (field) {
+          if (field.in) {
+            const name = field.map || key
+            ;(params[field.in] as Record<string, unknown>)[name] = value
+          } else {
+            params[field.map] = value
+          }
+        } else {
+          const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix))
+
+          if (extra) {
+            const [prefix, slot] = extra
+            ;(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value
+          } else if ("allowExtra" in config && config.allowExtra) {
+            for (const [slot, allowed] of Object.entries(config.allowExtra)) {
+              if (allowed) {
+                ;(params[slot as Slot] as Record<string, unknown>)[key] = value
+                break
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  stripEmptySlots(params)
+
+  return params
+}

+ 167 - 0
packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts

@@ -0,0 +1,167 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}
+
+interface SerializePrimitiveOptions {
+  allowReserved?: boolean
+  name: string
+}
+
+export interface SerializerOptions<T> {
+  /**
+   * @default true
+   */
+  explode: boolean
+  style: T
+}
+
+export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"
+export type ArraySeparatorStyle = ArrayStyle | MatrixStyle
+type MatrixStyle = "label" | "matrix" | "simple"
+export type ObjectStyle = "form" | "deepObject"
+type ObjectSeparatorStyle = ObjectStyle | MatrixStyle
+
+interface SerializePrimitiveParam extends SerializePrimitiveOptions {
+  value: string
+}
+
+export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
+  switch (style) {
+    case "label":
+      return "."
+    case "matrix":
+      return ";"
+    case "simple":
+      return ","
+    default:
+      return "&"
+  }
+}
+
+export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
+  switch (style) {
+    case "form":
+      return ","
+    case "pipeDelimited":
+      return "|"
+    case "spaceDelimited":
+      return "%20"
+    default:
+      return ","
+  }
+}
+
+export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
+  switch (style) {
+    case "label":
+      return "."
+    case "matrix":
+      return ";"
+    case "simple":
+      return ","
+    default:
+      return "&"
+  }
+}
+
+export const serializeArrayParam = ({
+  allowReserved,
+  explode,
+  name,
+  style,
+  value,
+}: SerializeOptions<ArraySeparatorStyle> & {
+  value: unknown[]
+}) => {
+  if (!explode) {
+    const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
+      separatorArrayNoExplode(style),
+    )
+    switch (style) {
+      case "label":
+        return `.${joinedValues}`
+      case "matrix":
+        return `;${name}=${joinedValues}`
+      case "simple":
+        return joinedValues
+      default:
+        return `${name}=${joinedValues}`
+    }
+  }
+
+  const separator = separatorArrayExplode(style)
+  const joinedValues = value
+    .map((v) => {
+      if (style === "label" || style === "simple") {
+        return allowReserved ? v : encodeURIComponent(v as string)
+      }
+
+      return serializePrimitiveParam({
+        allowReserved,
+        name,
+        value: v as string,
+      })
+    })
+    .join(separator)
+  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}
+
+export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
+  if (value === undefined || value === null) {
+    return ""
+  }
+
+  if (typeof value === "object") {
+    throw new Error(
+      "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.",
+    )
+  }
+
+  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`
+}
+
+export const serializeObjectParam = ({
+  allowReserved,
+  explode,
+  name,
+  style,
+  value,
+  valueOnly,
+}: SerializeOptions<ObjectSeparatorStyle> & {
+  value: Record<string, unknown> | Date
+  valueOnly?: boolean
+}) => {
+  if (value instanceof Date) {
+    return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`
+  }
+
+  if (style !== "deepObject" && !explode) {
+    let values: string[] = []
+    Object.entries(value).forEach(([key, v]) => {
+      values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]
+    })
+    const joinedValues = values.join(",")
+    switch (style) {
+      case "form":
+        return `${name}=${joinedValues}`
+      case "label":
+        return `.${joinedValues}`
+      case "matrix":
+        return `;${name}=${joinedValues}`
+      default:
+        return joinedValues
+    }
+  }
+
+  const separator = separatorObjectExplode(style)
+  const joinedValues = Object.entries(value)
+    .map(([key, v]) =>
+      serializePrimitiveParam({
+        allowReserved,
+        name: style === "deepObject" ? `${name}[${key}]` : key,
+        value: v as string,
+      }),
+    )
+    .join(separator)
+  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}

+ 111 - 0
packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts

@@ -0,0 +1,111 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+/**
+ * JSON-friendly union that mirrors what Pinia Colada can hash.
+ */
+export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }
+
+/**
+ * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
+ */
+export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
+  if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+    return undefined
+  }
+  if (typeof value === "bigint") {
+    return value.toString()
+  }
+  if (value instanceof Date) {
+    return value.toISOString()
+  }
+  return value
+}
+
+/**
+ * Safely stringifies a value and parses it back into a JsonValue.
+ */
+export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
+  try {
+    const json = JSON.stringify(input, queryKeyJsonReplacer)
+    if (json === undefined) {
+      return undefined
+    }
+    return JSON.parse(json) as JsonValue
+  } catch {
+    return undefined
+  }
+}
+
+/**
+ * Detects plain objects (including objects with a null prototype).
+ */
+const isPlainObject = (value: unknown): value is Record<string, unknown> => {
+  if (value === null || typeof value !== "object") {
+    return false
+  }
+  const prototype = Object.getPrototypeOf(value as object)
+  return prototype === Object.prototype || prototype === null
+}
+
+/**
+ * Turns URLSearchParams into a sorted JSON object for deterministic keys.
+ */
+const serializeSearchParams = (params: URLSearchParams): JsonValue => {
+  const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b))
+  const result: Record<string, JsonValue> = {}
+
+  for (const [key, value] of entries) {
+    const existing = result[key]
+    if (existing === undefined) {
+      result[key] = value
+      continue
+    }
+
+    if (Array.isArray(existing)) {
+      ;(existing as string[]).push(value)
+    } else {
+      result[key] = [existing, value]
+    }
+  }
+
+  return result
+}
+
+/**
+ * Normalizes any accepted value into a JSON-friendly shape for query keys.
+ */
+export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
+  if (value === null) {
+    return null
+  }
+
+  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+    return value
+  }
+
+  if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+    return undefined
+  }
+
+  if (typeof value === "bigint") {
+    return value.toString()
+  }
+
+  if (value instanceof Date) {
+    return value.toISOString()
+  }
+
+  if (Array.isArray(value)) {
+    return stringifyToJsonValue(value)
+  }
+
+  if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
+    return serializeSearchParams(value)
+  }
+
+  if (isPlainObject(value)) {
+    return stringifyToJsonValue(value)
+  }
+
+  return undefined
+}

+ 237 - 0
packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts

@@ -0,0 +1,237 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Config } from "./types.gen.js"
+
+export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> &
+  Pick<Config, "method" | "responseTransformer" | "responseValidator"> & {
+    /**
+     * Fetch API implementation. You can use this option to provide a custom
+     * fetch instance.
+     *
+     * @default globalThis.fetch
+     */
+    fetch?: typeof fetch
+    /**
+     * Implementing clients can call request interceptors inside this hook.
+     */
+    onRequest?: (url: string, init: RequestInit) => Promise<Request>
+    /**
+     * Callback invoked when a network or parsing error occurs during streaming.
+     *
+     * This option applies only if the endpoint returns a stream of events.
+     *
+     * @param error The error that occurred.
+     */
+    onSseError?: (error: unknown) => void
+    /**
+     * Callback invoked when an event is streamed from the server.
+     *
+     * This option applies only if the endpoint returns a stream of events.
+     *
+     * @param event Event streamed from the server.
+     * @returns Nothing (void).
+     */
+    onSseEvent?: (event: StreamEvent<TData>) => void
+    serializedBody?: RequestInit["body"]
+    /**
+     * Default retry delay in milliseconds.
+     *
+     * This option applies only if the endpoint returns a stream of events.
+     *
+     * @default 3000
+     */
+    sseDefaultRetryDelay?: number
+    /**
+     * Maximum number of retry attempts before giving up.
+     */
+    sseMaxRetryAttempts?: number
+    /**
+     * Maximum retry delay in milliseconds.
+     *
+     * Applies only when exponential backoff is used.
+     *
+     * This option applies only if the endpoint returns a stream of events.
+     *
+     * @default 30000
+     */
+    sseMaxRetryDelay?: number
+    /**
+     * Optional sleep function for retry backoff.
+     *
+     * Defaults to using `setTimeout`.
+     */
+    sseSleepFn?: (ms: number) => Promise<void>
+    url: string
+  }
+
+export interface StreamEvent<TData = unknown> {
+  data: TData
+  event?: string
+  id?: string
+  retry?: number
+}
+
+export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
+  stream: AsyncGenerator<TData extends Record<string, unknown> ? TData[keyof TData] : TData, TReturn, TNext>
+}
+
+export const createSseClient = <TData = unknown>({
+  onRequest,
+  onSseError,
+  onSseEvent,
+  responseTransformer,
+  responseValidator,
+  sseDefaultRetryDelay,
+  sseMaxRetryAttempts,
+  sseMaxRetryDelay,
+  sseSleepFn,
+  url,
+  ...options
+}: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
+  let lastEventId: string | undefined
+
+  const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)))
+
+  const createStream = async function* () {
+    let retryDelay: number = sseDefaultRetryDelay ?? 3000
+    let attempt = 0
+    const signal = options.signal ?? new AbortController().signal
+
+    while (true) {
+      if (signal.aborted) break
+
+      attempt++
+
+      const headers =
+        options.headers instanceof Headers
+          ? options.headers
+          : new Headers(options.headers as Record<string, string> | undefined)
+
+      if (lastEventId !== undefined) {
+        headers.set("Last-Event-ID", lastEventId)
+      }
+
+      try {
+        const requestInit: RequestInit = {
+          redirect: "follow",
+          ...options,
+          body: options.serializedBody,
+          headers,
+          signal,
+        }
+        let request = new Request(url, requestInit)
+        if (onRequest) {
+          request = await onRequest(url, requestInit)
+        }
+        // fetch must be assigned here, otherwise it would throw the error:
+        // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
+        const _fetch = options.fetch ?? globalThis.fetch
+        const response = await _fetch(request)
+
+        if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`)
+
+        if (!response.body) throw new Error("No body in SSE response")
+
+        const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
+
+        let buffer = ""
+
+        const abortHandler = () => {
+          try {
+            reader.cancel()
+          } catch {
+            // noop
+          }
+        }
+
+        signal.addEventListener("abort", abortHandler)
+
+        try {
+          while (true) {
+            const { done, value } = await reader.read()
+            if (done) break
+            buffer += value
+
+            const chunks = buffer.split("\n\n")
+            buffer = chunks.pop() ?? ""
+
+            for (const chunk of chunks) {
+              const lines = chunk.split("\n")
+              const dataLines: Array<string> = []
+              let eventName: string | undefined
+
+              for (const line of lines) {
+                if (line.startsWith("data:")) {
+                  dataLines.push(line.replace(/^data:\s*/, ""))
+                } else if (line.startsWith("event:")) {
+                  eventName = line.replace(/^event:\s*/, "")
+                } else if (line.startsWith("id:")) {
+                  lastEventId = line.replace(/^id:\s*/, "")
+                } else if (line.startsWith("retry:")) {
+                  const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10)
+                  if (!Number.isNaN(parsed)) {
+                    retryDelay = parsed
+                  }
+                }
+              }
+
+              let data: unknown
+              let parsedJson = false
+
+              if (dataLines.length) {
+                const rawData = dataLines.join("\n")
+                try {
+                  data = JSON.parse(rawData)
+                  parsedJson = true
+                } catch {
+                  data = rawData
+                }
+              }
+
+              if (parsedJson) {
+                if (responseValidator) {
+                  await responseValidator(data)
+                }
+
+                if (responseTransformer) {
+                  data = await responseTransformer(data)
+                }
+              }
+
+              onSseEvent?.({
+                data,
+                event: eventName,
+                id: lastEventId,
+                retry: retryDelay,
+              })
+
+              if (dataLines.length) {
+                yield data as any
+              }
+            }
+          }
+        } finally {
+          signal.removeEventListener("abort", abortHandler)
+          reader.releaseLock()
+        }
+
+        break // exit loop on normal completion
+      } catch (error) {
+        // connection failed or aborted; retry after delay
+        onSseError?.(error)
+
+        if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
+          break // stop after firing error
+        }
+
+        // exponential backoff: double retry each attempt, cap at 30s
+        const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000)
+        await sleep(backoff)
+      }
+    }
+  }
+
+  const stream = createStream()
+
+  return { stream }
+}

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

@@ -0,0 +1,86 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Auth, AuthToken } from "./auth.gen.js"
+import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
+
+export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"
+
+export type Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
+  /**
+   * Returns the final request URL.
+   */
+  buildUrl: BuildUrlFn
+  getConfig: () => Config
+  request: RequestFn
+  setConfig: (config: Config) => Config
+} & {
+  [K in HttpMethod]: MethodFn
+} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } })
+
+export interface Config {
+  /**
+   * Auth token or a function returning auth token. The resolved value will be
+   * added to the request payload as defined by its `security` array.
+   */
+  auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken
+  /**
+   * A function for serializing request body parameter. By default,
+   * {@link JSON.stringify()} will be used.
+   */
+  bodySerializer?: BodySerializer | null
+  /**
+   * An object containing any HTTP headers that you want to pre-populate your
+   * `Headers` object with.
+   *
+   * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
+   */
+  headers?:
+    | RequestInit["headers"]
+    | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>
+  /**
+   * The request method.
+   *
+   * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
+   */
+  method?: Uppercase<HttpMethod>
+  /**
+   * A function for serializing request query parameters. By default, arrays
+   * will be exploded in form style, objects will be exploded in deepObject
+   * style, and reserved characters are percent-encoded.
+   *
+   * This method will have no effect if the native `paramsSerializer()` Axios
+   * API function is used.
+   *
+   * {@link https://swagger.io/docs/specification/serialization/#query View examples}
+   */
+  querySerializer?: QuerySerializer | QuerySerializerOptions
+  /**
+   * A function validating request data. This is useful if you want to ensure
+   * the request conforms to the desired shape, so it can be safely sent to
+   * the server.
+   */
+  requestValidator?: (data: unknown) => Promise<unknown>
+  /**
+   * A function transforming response data before it's returned. This is useful
+   * for post-processing data, e.g. converting ISO strings into Date objects.
+   */
+  responseTransformer?: (data: unknown) => Promise<unknown>
+  /**
+   * A function validating response data. This is useful if you want to ensure
+   * the response conforms to the desired shape, so it can be safely passed to
+   * the transformers and returned to the user.
+   */
+  responseValidator?: (data: unknown) => Promise<unknown>
+}
+
+type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
+  ? true
+  : [T] extends [never | undefined]
+    ? [undefined] extends [T]
+      ? false
+      : true
+    : false
+
+export type OmitNever<T extends Record<string, unknown>> = {
+  [K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true ? never : K]: T[K]
+}

+ 137 - 0
packages/sdk/js/src/v2/gen/core/utils.gen.ts

@@ -0,0 +1,137 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen.js"
+import {
+  type ArraySeparatorStyle,
+  serializeArrayParam,
+  serializeObjectParam,
+  serializePrimitiveParam,
+} from "./pathSerializer.gen.js"
+
+export interface PathSerializer {
+  path: Record<string, unknown>
+  url: string
+}
+
+export const PATH_PARAM_RE = /\{[^{}]+\}/g
+
+export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
+  let url = _url
+  const matches = _url.match(PATH_PARAM_RE)
+  if (matches) {
+    for (const match of matches) {
+      let explode = false
+      let name = match.substring(1, match.length - 1)
+      let style: ArraySeparatorStyle = "simple"
+
+      if (name.endsWith("*")) {
+        explode = true
+        name = name.substring(0, name.length - 1)
+      }
+
+      if (name.startsWith(".")) {
+        name = name.substring(1)
+        style = "label"
+      } else if (name.startsWith(";")) {
+        name = name.substring(1)
+        style = "matrix"
+      }
+
+      const value = path[name]
+
+      if (value === undefined || value === null) {
+        continue
+      }
+
+      if (Array.isArray(value)) {
+        url = url.replace(match, serializeArrayParam({ explode, name, style, value }))
+        continue
+      }
+
+      if (typeof value === "object") {
+        url = url.replace(
+          match,
+          serializeObjectParam({
+            explode,
+            name,
+            style,
+            value: value as Record<string, unknown>,
+            valueOnly: true,
+          }),
+        )
+        continue
+      }
+
+      if (style === "matrix") {
+        url = url.replace(
+          match,
+          `;${serializePrimitiveParam({
+            name,
+            value: value as string,
+          })}`,
+        )
+        continue
+      }
+
+      const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string))
+      url = url.replace(match, replaceValue)
+    }
+  }
+  return url
+}
+
+export const getUrl = ({
+  baseUrl,
+  path,
+  query,
+  querySerializer,
+  url: _url,
+}: {
+  baseUrl?: string
+  path?: Record<string, unknown>
+  query?: Record<string, unknown>
+  querySerializer: QuerySerializer
+  url: string
+}) => {
+  const pathUrl = _url.startsWith("/") ? _url : `/${_url}`
+  let url = (baseUrl ?? "") + pathUrl
+  if (path) {
+    url = defaultPathSerializer({ path, url })
+  }
+  let search = query ? querySerializer(query) : ""
+  if (search.startsWith("?")) {
+    search = search.substring(1)
+  }
+  if (search) {
+    url += `?${search}`
+  }
+  return url
+}
+
+export function getValidRequestBody(options: {
+  body?: unknown
+  bodySerializer?: BodySerializer | null
+  serializedBody?: unknown
+}) {
+  const hasBody = options.body !== undefined
+  const isSerializedBody = hasBody && options.bodySerializer
+
+  if (isSerializedBody) {
+    if ("serializedBody" in options) {
+      const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== ""
+
+      return hasSerializedBody ? options.serializedBody : null
+    }
+
+    // not all clients implement a serializedBody property (i.e. client-axios)
+    return options.body !== "" ? options.body : null
+  }
+
+  // plain/text body
+  if (hasBody) {
+    return options.body
+  }
+
+  // no body was provided
+  return undefined
+}

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

@@ -0,0 +1,2343 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import { client } from "./client.gen.js"
+import { buildClientParams, type Client, type Options as Options2, type TDataShape } from "./client/index.js"
+import type {
+  AgentPartInput,
+  AppAgentsResponses,
+  AppLogErrors,
+  AppLogResponses,
+  Auth as Auth2,
+  AuthSetErrors,
+  AuthSetResponses,
+  CommandListResponses,
+  Config as Config2,
+  ConfigGetResponses,
+  ConfigProvidersResponses,
+  ConfigUpdateErrors,
+  ConfigUpdateResponses,
+  EventSubscribeResponses,
+  EventTuiCommandExecute,
+  EventTuiPromptAppend,
+  EventTuiToastShow,
+  FileListResponses,
+  FilePartInput,
+  FileReadResponses,
+  FileStatusResponses,
+  FindFilesResponses,
+  FindSymbolsResponses,
+  FindTextResponses,
+  FormatterStatusResponses,
+  GlobalEventResponses,
+  InstanceDisposeResponses,
+  LspStatusResponses,
+  McpAddErrors,
+  McpAddResponses,
+  McpAuthAuthenticateErrors,
+  McpAuthAuthenticateResponses,
+  McpAuthCallbackErrors,
+  McpAuthCallbackResponses,
+  McpAuthRemoveErrors,
+  McpAuthRemoveResponses,
+  McpAuthStartErrors,
+  McpAuthStartResponses,
+  McpLocalConfig,
+  McpRemoteConfig,
+  McpStatusResponses,
+  PathGetResponses,
+  PermissionRespondErrors,
+  PermissionRespondResponses,
+  ProjectCurrentResponses,
+  ProjectListResponses,
+  ProviderAuthResponses,
+  ProviderListResponses,
+  ProviderOauthAuthorizeErrors,
+  ProviderOauthAuthorizeResponses,
+  ProviderOauthCallbackErrors,
+  ProviderOauthCallbackResponses,
+  PtyConnectErrors,
+  PtyConnectResponses,
+  PtyCreateErrors,
+  PtyCreateResponses,
+  PtyGetErrors,
+  PtyGetResponses,
+  PtyListResponses,
+  PtyRemoveErrors,
+  PtyRemoveResponses,
+  PtyUpdateErrors,
+  PtyUpdateResponses,
+  SessionAbortErrors,
+  SessionAbortResponses,
+  SessionChildrenErrors,
+  SessionChildrenResponses,
+  SessionCommandErrors,
+  SessionCommandResponses,
+  SessionCreateErrors,
+  SessionCreateResponses,
+  SessionDeleteErrors,
+  SessionDeleteResponses,
+  SessionDiffErrors,
+  SessionDiffResponses,
+  SessionForkResponses,
+  SessionGetErrors,
+  SessionGetResponses,
+  SessionInitErrors,
+  SessionInitResponses,
+  SessionListResponses,
+  SessionMessageErrors,
+  SessionMessageResponses,
+  SessionMessagesErrors,
+  SessionMessagesResponses,
+  SessionPromptAsyncErrors,
+  SessionPromptAsyncResponses,
+  SessionPromptErrors,
+  SessionPromptResponses,
+  SessionRevertErrors,
+  SessionRevertResponses,
+  SessionShareErrors,
+  SessionShareResponses,
+  SessionShellErrors,
+  SessionShellResponses,
+  SessionStatusErrors,
+  SessionStatusResponses,
+  SessionSummarizeErrors,
+  SessionSummarizeResponses,
+  SessionTodoErrors,
+  SessionTodoResponses,
+  SessionUnrevertErrors,
+  SessionUnrevertResponses,
+  SessionUnshareErrors,
+  SessionUnshareResponses,
+  SessionUpdateErrors,
+  SessionUpdateResponses,
+  SubtaskPartInput,
+  TextPartInput,
+  ToolIdsErrors,
+  ToolIdsResponses,
+  ToolListErrors,
+  ToolListResponses,
+  TuiAppendPromptErrors,
+  TuiAppendPromptResponses,
+  TuiClearPromptResponses,
+  TuiControlNextResponses,
+  TuiControlResponseResponses,
+  TuiExecuteCommandErrors,
+  TuiExecuteCommandResponses,
+  TuiOpenHelpResponses,
+  TuiOpenModelsResponses,
+  TuiOpenSessionsResponses,
+  TuiOpenThemesResponses,
+  TuiPublishErrors,
+  TuiPublishResponses,
+  TuiShowToastResponses,
+  TuiSubmitPromptResponses,
+  VcsGetResponses,
+} from "./types.gen.js"
+
+export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<
+  TData,
+  ThrowOnError
+> & {
+  /**
+   * You can provide a client instance returned by `createClient()` instead of
+   * individual options. This might be also useful if you want to implement a
+   * custom client.
+   */
+  client?: Client
+  /**
+   * You can pass arbitrary values through the `meta` object. This can be
+   * used to access values that aren't defined as part of the SDK function.
+   */
+  meta?: Record<string, unknown>
+}
+
+class HeyApiClient {
+  protected client: Client
+
+  constructor(args?: { client?: Client }) {
+    this.client = args?.client ?? client
+  }
+}
+
+class HeyApiRegistry<T> {
+  private readonly defaultKey = "default"
+
+  private readonly instances: Map<string, T> = new Map()
+
+  get(key?: string): T {
+    const instance = this.instances.get(key ?? this.defaultKey)
+    if (!instance) {
+      throw new Error(`No SDK client found. Create one with "new OpencodeClient()" to fix this error.`)
+    }
+    return instance
+  }
+
+  set(value: T, key?: string): void {
+    this.instances.set(key ?? this.defaultKey, value)
+  }
+}
+
+export class Global extends HeyApiClient {
+  /**
+   * Get events
+   */
+  public event<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
+    return (options?.client ?? this.client).sse.get<GlobalEventResponses, unknown, ThrowOnError>({
+      url: "/global/event",
+      ...options,
+    })
+  }
+}
+
+export class Project extends HeyApiClient {
+  /**
+   * List all projects
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ProjectListResponses, unknown, ThrowOnError>({
+      url: "/project",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get the current project
+   */
+  public current<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ProjectCurrentResponses, unknown, ThrowOnError>({
+      url: "/project/current",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Pty extends HeyApiClient {
+  /**
+   * List all PTY sessions
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<PtyListResponses, unknown, ThrowOnError>({
+      url: "/pty",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Create a new PTY session
+   */
+  public create<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      command?: string
+      args?: Array<string>
+      cwd?: string
+      title?: string
+      env?: {
+        [key: string]: string
+      }
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "command" },
+            { in: "body", key: "args" },
+            { in: "body", key: "cwd" },
+            { in: "body", key: "title" },
+            { in: "body", key: "env" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<PtyCreateResponses, PtyCreateErrors, ThrowOnError>({
+      url: "/pty",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Remove a PTY session
+   */
+  public remove<ThrowOnError extends boolean = false>(
+    parameters: {
+      ptyID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "ptyID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).delete<PtyRemoveResponses, PtyRemoveErrors, ThrowOnError>({
+      url: "/pty/{ptyID}",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get PTY session info
+   */
+  public get<ThrowOnError extends boolean = false>(
+    parameters: {
+      ptyID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "ptyID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<PtyGetResponses, PtyGetErrors, ThrowOnError>({
+      url: "/pty/{ptyID}",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Update PTY session
+   */
+  public update<ThrowOnError extends boolean = false>(
+    parameters: {
+      ptyID: string
+      directory?: string
+      title?: string
+      size?: {
+        rows: number
+        cols: number
+      }
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "ptyID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "title" },
+            { in: "body", key: "size" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).put<PtyUpdateResponses, PtyUpdateErrors, ThrowOnError>({
+      url: "/pty/{ptyID}",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Connect to a PTY session
+   */
+  public connect<ThrowOnError extends boolean = false>(
+    parameters: {
+      ptyID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "ptyID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<PtyConnectResponses, PtyConnectErrors, ThrowOnError>({
+      url: "/pty/{ptyID}/connect",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Config extends HeyApiClient {
+  /**
+   * Get config info
+   */
+  public get<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ConfigGetResponses, unknown, ThrowOnError>({
+      url: "/config",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Update config
+   */
+  public update<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      config?: Config2
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { key: "config", map: "body" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).patch<ConfigUpdateResponses, ConfigUpdateErrors, ThrowOnError>({
+      url: "/config",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * List all providers
+   */
+  public providers<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ConfigProvidersResponses, unknown, ThrowOnError>({
+      url: "/config/providers",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Tool extends HeyApiClient {
+  /**
+   * List all tool IDs (including built-in and dynamically registered)
+   */
+  public ids<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ToolIdsResponses, ToolIdsErrors, ThrowOnError>({
+      url: "/experimental/tool/ids",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * List tools with JSON schema parameters for a provider/model
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      provider: string
+      model: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "provider" },
+            { in: "query", key: "model" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<ToolListResponses, ToolListErrors, ThrowOnError>({
+      url: "/experimental/tool",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Instance extends HeyApiClient {
+  /**
+   * Dispose the current instance
+   */
+  public dispose<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<InstanceDisposeResponses, unknown, ThrowOnError>({
+      url: "/instance/dispose",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Path extends HeyApiClient {
+  /**
+   * Get the current path
+   */
+  public get<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<PathGetResponses, unknown, ThrowOnError>({
+      url: "/path",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Vcs extends HeyApiClient {
+  /**
+   * Get VCS info for the current instance
+   */
+  public get<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<VcsGetResponses, unknown, ThrowOnError>({
+      url: "/vcs",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Session extends HeyApiClient {
+  /**
+   * List all sessions
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<SessionListResponses, unknown, ThrowOnError>({
+      url: "/session",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Create a new session
+   */
+  public create<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      parentID?: string
+      title?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "parentID" },
+            { in: "body", key: "title" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionCreateResponses, SessionCreateErrors, ThrowOnError>({
+      url: "/session",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Get session status
+   */
+  public status<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<SessionStatusResponses, SessionStatusErrors, ThrowOnError>({
+      url: "/session/status",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Delete a session and all its data
+   */
+  public delete<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).delete<SessionDeleteResponses, SessionDeleteErrors, ThrowOnError>({
+      url: "/session/{sessionID}",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get session
+   */
+  public get<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionGetResponses, SessionGetErrors, ThrowOnError>({
+      url: "/session/{sessionID}",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Update session properties
+   */
+  public update<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      title?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "title" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).patch<SessionUpdateResponses, SessionUpdateErrors, ThrowOnError>({
+      url: "/session/{sessionID}",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Get a session's children
+   */
+  public children<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionChildrenResponses, SessionChildrenErrors, ThrowOnError>({
+      url: "/session/{sessionID}/children",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get the todo list for a session
+   */
+  public todo<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionTodoResponses, SessionTodoErrors, ThrowOnError>({
+      url: "/session/{sessionID}/todo",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Analyze the app and create an AGENTS.md file
+   */
+  public init<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      modelID?: string
+      providerID?: string
+      messageID?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "modelID" },
+            { in: "body", key: "providerID" },
+            { in: "body", key: "messageID" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionInitResponses, SessionInitErrors, ThrowOnError>({
+      url: "/session/{sessionID}/init",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Fork an existing session at a specific message
+   */
+  public fork<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "messageID" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionForkResponses, unknown, ThrowOnError>({
+      url: "/session/{sessionID}/fork",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Abort a session
+   */
+  public abort<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionAbortResponses, SessionAbortErrors, ThrowOnError>({
+      url: "/session/{sessionID}/abort",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Unshare the session
+   */
+  public unshare<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).delete<SessionUnshareResponses, SessionUnshareErrors, ThrowOnError>({
+      url: "/session/{sessionID}/share",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Share a session
+   */
+  public share<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionShareResponses, SessionShareErrors, ThrowOnError>({
+      url: "/session/{sessionID}/share",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get the diff for this session
+   */
+  public diff<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "query", key: "messageID" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionDiffResponses, SessionDiffErrors, ThrowOnError>({
+      url: "/session/{sessionID}/diff",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Summarize the session
+   */
+  public summarize<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      providerID?: string
+      modelID?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "providerID" },
+            { in: "body", key: "modelID" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionSummarizeResponses, SessionSummarizeErrors, ThrowOnError>({
+      url: "/session/{sessionID}/summarize",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * List messages for a session
+   */
+  public messages<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      limit?: number
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "query", key: "limit" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionMessagesResponses, SessionMessagesErrors, ThrowOnError>({
+      url: "/session/{sessionID}/message",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Create and send a new message to a session
+   */
+  public prompt<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+      model?: {
+        providerID: string
+        modelID: string
+      }
+      agent?: string
+      noReply?: boolean
+      system?: string
+      tools?: {
+        [key: string]: boolean
+      }
+      parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "messageID" },
+            { in: "body", key: "model" },
+            { in: "body", key: "agent" },
+            { in: "body", key: "noReply" },
+            { in: "body", key: "system" },
+            { in: "body", key: "tools" },
+            { in: "body", key: "parts" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionPromptResponses, SessionPromptErrors, ThrowOnError>({
+      url: "/session/{sessionID}/message",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Get a message from a session
+   */
+  public message<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      messageID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "path", key: "messageID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<SessionMessageResponses, SessionMessageErrors, ThrowOnError>({
+      url: "/session/{sessionID}/message/{messageID}",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Create and send a new message to a session, start if needed and return immediately
+   */
+  public promptAsync<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+      model?: {
+        providerID: string
+        modelID: string
+      }
+      agent?: string
+      noReply?: boolean
+      system?: string
+      tools?: {
+        [key: string]: boolean
+      }
+      parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "messageID" },
+            { in: "body", key: "model" },
+            { in: "body", key: "agent" },
+            { in: "body", key: "noReply" },
+            { in: "body", key: "system" },
+            { in: "body", key: "tools" },
+            { in: "body", key: "parts" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionPromptAsyncResponses, SessionPromptAsyncErrors, ThrowOnError>({
+      url: "/session/{sessionID}/prompt_async",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Send a new command to a session
+   */
+  public command<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+      agent?: string
+      model?: string
+      arguments?: string
+      command?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "messageID" },
+            { in: "body", key: "agent" },
+            { in: "body", key: "model" },
+            { in: "body", key: "arguments" },
+            { in: "body", key: "command" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionCommandResponses, SessionCommandErrors, ThrowOnError>({
+      url: "/session/{sessionID}/command",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Run a shell command
+   */
+  public shell<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      agent?: string
+      model?: {
+        providerID: string
+        modelID: string
+      }
+      command?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "agent" },
+            { in: "body", key: "model" },
+            { in: "body", key: "command" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionShellResponses, SessionShellErrors, ThrowOnError>({
+      url: "/session/{sessionID}/shell",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Revert a message
+   */
+  public revert<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+      messageID?: string
+      partID?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "messageID" },
+            { in: "body", key: "partID" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionRevertResponses, SessionRevertErrors, ThrowOnError>({
+      url: "/session/{sessionID}/revert",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Restore all reverted messages
+   */
+  public unrevert<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<SessionUnrevertResponses, SessionUnrevertErrors, ThrowOnError>({
+      url: "/session/{sessionID}/unrevert",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Permission extends HeyApiClient {
+  /**
+   * Respond to a permission request
+   */
+  public respond<ThrowOnError extends boolean = false>(
+    parameters: {
+      sessionID: string
+      permissionID: string
+      directory?: string
+      response?: "once" | "always" | "reject"
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "sessionID" },
+            { in: "path", key: "permissionID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "response" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<PermissionRespondResponses, PermissionRespondErrors, ThrowOnError>({
+      url: "/session/{sessionID}/permissions/{permissionID}",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+}
+
+export class Command extends HeyApiClient {
+  /**
+   * List all commands
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<CommandListResponses, unknown, ThrowOnError>({
+      url: "/command",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Oauth extends HeyApiClient {
+  /**
+   * Authorize a provider using OAuth
+   */
+  public authorize<ThrowOnError extends boolean = false>(
+    parameters: {
+      providerID: string
+      directory?: string
+      method?: number
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "providerID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "method" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<
+      ProviderOauthAuthorizeResponses,
+      ProviderOauthAuthorizeErrors,
+      ThrowOnError
+    >({
+      url: "/provider/{providerID}/oauth/authorize",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Handle OAuth callback for a provider
+   */
+  public callback<ThrowOnError extends boolean = false>(
+    parameters: {
+      providerID: string
+      directory?: string
+      method?: number
+      code?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "providerID" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "method" },
+            { in: "body", key: "code" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<
+      ProviderOauthCallbackResponses,
+      ProviderOauthCallbackErrors,
+      ThrowOnError
+    >({
+      url: "/provider/{providerID}/oauth/callback",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+}
+
+export class Provider extends HeyApiClient {
+  /**
+   * List all providers
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ProviderListResponses, unknown, ThrowOnError>({
+      url: "/provider",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get provider authentication methods
+   */
+  public auth<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<ProviderAuthResponses, unknown, ThrowOnError>({
+      url: "/provider/auth",
+      ...options,
+      ...params,
+    })
+  }
+
+  oauth = new Oauth({ client: this.client })
+}
+
+export class Find extends HeyApiClient {
+  /**
+   * Find text in files
+   */
+  public text<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      pattern: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "pattern" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<FindTextResponses, unknown, ThrowOnError>({
+      url: "/find",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Find files
+   */
+  public files<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      query: string
+      dirs?: "true" | "false"
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "query" },
+            { in: "query", key: "dirs" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<FindFilesResponses, unknown, ThrowOnError>({
+      url: "/find/file",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Find workspace symbols
+   */
+  public symbols<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      query: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "query" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<FindSymbolsResponses, unknown, ThrowOnError>({
+      url: "/find/symbol",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class File extends HeyApiClient {
+  /**
+   * List files and directories
+   */
+  public list<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      path: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "path" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<FileListResponses, unknown, ThrowOnError>({
+      url: "/file",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Read a file
+   */
+  public read<ThrowOnError extends boolean = false>(
+    parameters: {
+      directory?: string
+      path: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "query", key: "path" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).get<FileReadResponses, unknown, ThrowOnError>({
+      url: "/file/content",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Get file status
+   */
+  public status<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<FileStatusResponses, unknown, ThrowOnError>({
+      url: "/file/status",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class App extends HeyApiClient {
+  /**
+   * Write a log entry to the server logs
+   */
+  public log<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      service?: string
+      level?: "debug" | "info" | "error" | "warn"
+      message?: string
+      extra?: {
+        [key: string]: unknown
+      }
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "service" },
+            { in: "body", key: "level" },
+            { in: "body", key: "message" },
+            { in: "body", key: "extra" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<AppLogResponses, AppLogErrors, ThrowOnError>({
+      url: "/log",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * List all agents
+   */
+  public agents<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<AppAgentsResponses, unknown, ThrowOnError>({
+      url: "/agent",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Auth extends HeyApiClient {
+  /**
+   * Remove OAuth credentials for an MCP server
+   */
+  public remove<ThrowOnError extends boolean = false>(
+    parameters: {
+      name: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "name" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).delete<McpAuthRemoveResponses, McpAuthRemoveErrors, ThrowOnError>({
+      url: "/mcp/{name}/auth",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Start OAuth authentication flow for an MCP server
+   */
+  public start<ThrowOnError extends boolean = false>(
+    parameters: {
+      name: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "name" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<McpAuthStartResponses, McpAuthStartErrors, ThrowOnError>({
+      url: "/mcp/{name}/auth",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Complete OAuth authentication with authorization code
+   */
+  public callback<ThrowOnError extends boolean = false>(
+    parameters: {
+      name: string
+      directory?: string
+      code?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "name" },
+            { in: "query", key: "directory" },
+            { in: "body", key: "code" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<McpAuthCallbackResponses, McpAuthCallbackErrors, ThrowOnError>({
+      url: "/mcp/{name}/auth/callback",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Start OAuth flow and wait for callback (opens browser)
+   */
+  public authenticate<ThrowOnError extends boolean = false>(
+    parameters: {
+      name: string
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "name" },
+            { in: "query", key: "directory" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<McpAuthAuthenticateResponses, McpAuthAuthenticateErrors, ThrowOnError>(
+      {
+        url: "/mcp/{name}/auth/authenticate",
+        ...options,
+        ...params,
+      },
+    )
+  }
+
+  /**
+   * Set authentication credentials
+   */
+  public set<ThrowOnError extends boolean = false>(
+    parameters: {
+      providerID: string
+      directory?: string
+      auth?: Auth2
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "path", key: "providerID" },
+            { in: "query", key: "directory" },
+            { key: "auth", map: "body" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).put<AuthSetResponses, AuthSetErrors, ThrowOnError>({
+      url: "/auth/{providerID}",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+}
+
+export class Mcp extends HeyApiClient {
+  /**
+   * Get MCP server status
+   */
+  public status<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<McpStatusResponses, unknown, ThrowOnError>({
+      url: "/mcp",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Add MCP server dynamically
+   */
+  public add<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      name?: string
+      config?: McpLocalConfig | McpRemoteConfig
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "name" },
+            { in: "body", key: "config" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<McpAddResponses, McpAddErrors, ThrowOnError>({
+      url: "/mcp",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  auth = new Auth({ client: this.client })
+}
+
+export class Lsp extends HeyApiClient {
+  /**
+   * Get LSP server status
+   */
+  public status<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<LspStatusResponses, unknown, ThrowOnError>({
+      url: "/lsp",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Formatter extends HeyApiClient {
+  /**
+   * Get formatter status
+   */
+  public status<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<FormatterStatusResponses, unknown, ThrowOnError>({
+      url: "/formatter",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class Control extends HeyApiClient {
+  /**
+   * Get the next TUI request from the queue
+   */
+  public next<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).get<TuiControlNextResponses, unknown, ThrowOnError>({
+      url: "/tui/control/next",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Submit a response to the TUI request queue
+   */
+  public response<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      body?: unknown
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }, { in: "body" }] }])
+    return (options?.client ?? this.client).post<TuiControlResponseResponses, unknown, ThrowOnError>({
+      url: "/tui/control/response",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+}
+
+export class Tui extends HeyApiClient {
+  /**
+   * Append prompt to the TUI
+   */
+  public appendPrompt<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      text?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "text" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<TuiAppendPromptResponses, TuiAppendPromptErrors, ThrowOnError>({
+      url: "/tui/append-prompt",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Open the help dialog
+   */
+  public openHelp<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiOpenHelpResponses, unknown, ThrowOnError>({
+      url: "/tui/open-help",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Open the session dialog
+   */
+  public openSessions<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiOpenSessionsResponses, unknown, ThrowOnError>({
+      url: "/tui/open-sessions",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Open the theme dialog
+   */
+  public openThemes<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiOpenThemesResponses, unknown, ThrowOnError>({
+      url: "/tui/open-themes",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Open the model dialog
+   */
+  public openModels<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiOpenModelsResponses, unknown, ThrowOnError>({
+      url: "/tui/open-models",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Submit the prompt
+   */
+  public submitPrompt<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiSubmitPromptResponses, unknown, ThrowOnError>({
+      url: "/tui/submit-prompt",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Clear the prompt
+   */
+  public clearPrompt<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).post<TuiClearPromptResponses, unknown, ThrowOnError>({
+      url: "/tui/clear-prompt",
+      ...options,
+      ...params,
+    })
+  }
+
+  /**
+   * Execute a TUI command (e.g. agent_cycle)
+   */
+  public executeCommand<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      command?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "command" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<TuiExecuteCommandResponses, TuiExecuteCommandErrors, ThrowOnError>({
+      url: "/tui/execute-command",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Show a toast notification in the TUI
+   */
+  public showToast<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      title?: string
+      message?: string
+      variant?: "info" | "success" | "warning" | "error"
+      duration?: number
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams(
+      [parameters],
+      [
+        {
+          args: [
+            { in: "query", key: "directory" },
+            { in: "body", key: "title" },
+            { in: "body", key: "message" },
+            { in: "body", key: "variant" },
+            { in: "body", key: "duration" },
+          ],
+        },
+      ],
+    )
+    return (options?.client ?? this.client).post<TuiShowToastResponses, unknown, ThrowOnError>({
+      url: "/tui/show-toast",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  /**
+   * Publish a TUI event
+   */
+  public publish<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+      body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }, { in: "body" }] }])
+    return (options?.client ?? this.client).post<TuiPublishResponses, TuiPublishErrors, ThrowOnError>({
+      url: "/tui/publish",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+
+  control = new Control({ client: this.client })
+}
+
+export class Event extends HeyApiClient {
+  /**
+   * Get events
+   */
+  public subscribe<ThrowOnError extends boolean = false>(
+    parameters?: {
+      directory?: string
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
+    return (options?.client ?? this.client).sse.get<EventSubscribeResponses, unknown, ThrowOnError>({
+      url: "/event",
+      ...options,
+      ...params,
+    })
+  }
+}
+
+export class OpencodeClient extends HeyApiClient {
+  public static readonly __registry = new HeyApiRegistry<OpencodeClient>()
+
+  constructor(args?: { client?: Client; key?: string }) {
+    super(args)
+    OpencodeClient.__registry.set(this, args?.key)
+  }
+
+  global = new Global({ client: this.client })
+
+  project = new Project({ client: this.client })
+
+  pty = new Pty({ client: this.client })
+
+  config = new Config({ client: this.client })
+
+  tool = new Tool({ client: this.client })
+
+  instance = new Instance({ client: this.client })
+
+  path = new Path({ client: this.client })
+
+  vcs = new Vcs({ client: this.client })
+
+  session = new Session({ client: this.client })
+
+  permission = new Permission({ client: this.client })
+
+  command = new Command({ client: this.client })
+
+  provider = new Provider({ client: this.client })
+
+  find = new Find({ client: this.client })
+
+  file = new File({ client: this.client })
+
+  app = new App({ client: this.client })
+
+  mcp = new Mcp({ client: this.client })
+
+  lsp = new Lsp({ client: this.client })
+
+  formatter = new Formatter({ client: this.client })
+
+  tui = new Tui({ client: this.client })
+
+  auth = new Auth({ client: this.client })
+
+  event = new Event({ client: this.client })
+}

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

@@ -0,0 +1,3845 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type ClientOptions = {
+  baseUrl: `${string}://${string}` | (string & {})
+}
+
+export type EventServerInstanceDisposed = {
+  type: "server.instance.disposed"
+  properties: {
+    directory: string
+  }
+}
+
+export type EventInstallationUpdated = {
+  type: "installation.updated"
+  properties: {
+    version: string
+  }
+}
+
+export type EventInstallationUpdateAvailable = {
+  type: "installation.update-available"
+  properties: {
+    version: string
+  }
+}
+
+export type EventLspClientDiagnostics = {
+  type: "lsp.client.diagnostics"
+  properties: {
+    serverID: string
+    path: string
+  }
+}
+
+export type EventLspUpdated = {
+  type: "lsp.updated"
+  properties: {
+    [key: string]: unknown
+  }
+}
+
+export type FileDiff = {
+  file: string
+  before: string
+  after: string
+  additions: number
+  deletions: number
+}
+
+export type UserMessage = {
+  id: string
+  sessionID: string
+  role: "user"
+  time: {
+    created: number
+  }
+  summary?: {
+    title?: string
+    body?: string
+    diffs: Array<FileDiff>
+  }
+  agent: string
+  model: {
+    providerID: string
+    modelID: string
+  }
+  system?: string
+  tools?: {
+    [key: string]: boolean
+  }
+}
+
+export type ProviderAuthError = {
+  name: "ProviderAuthError"
+  data: {
+    providerID: string
+    message: string
+  }
+}
+
+export type UnknownError = {
+  name: "UnknownError"
+  data: {
+    message: string
+  }
+}
+
+export type MessageOutputLengthError = {
+  name: "MessageOutputLengthError"
+  data: {
+    [key: string]: unknown
+  }
+}
+
+export type MessageAbortedError = {
+  name: "MessageAbortedError"
+  data: {
+    message: string
+  }
+}
+
+export type ApiError = {
+  name: "APIError"
+  data: {
+    message: string
+    statusCode?: number
+    isRetryable: boolean
+    responseHeaders?: {
+      [key: string]: string
+    }
+    responseBody?: string
+  }
+}
+
+export type AssistantMessage = {
+  id: string
+  sessionID: string
+  role: "assistant"
+  time: {
+    created: number
+    completed?: number
+  }
+  error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
+  parentID: string
+  modelID: string
+  providerID: string
+  mode: string
+  path: {
+    cwd: string
+    root: string
+  }
+  summary?: boolean
+  cost: number
+  tokens: {
+    input: number
+    output: number
+    reasoning: number
+    cache: {
+      read: number
+      write: number
+    }
+  }
+  finish?: string
+}
+
+export type Message = UserMessage | AssistantMessage
+
+export type EventMessageUpdated = {
+  type: "message.updated"
+  properties: {
+    info: Message
+  }
+}
+
+export type EventMessageRemoved = {
+  type: "message.removed"
+  properties: {
+    sessionID: string
+    messageID: string
+  }
+}
+
+export type TextPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "text"
+  text: string
+  synthetic?: boolean
+  ignored?: boolean
+  time?: {
+    start: number
+    end?: number
+  }
+  metadata?: {
+    [key: string]: unknown
+  }
+}
+
+export type ReasoningPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "reasoning"
+  text: string
+  metadata?: {
+    [key: string]: unknown
+  }
+  time: {
+    start: number
+    end?: number
+  }
+}
+
+export type FilePartSourceText = {
+  value: string
+  start: number
+  end: number
+}
+
+export type FileSource = {
+  text: FilePartSourceText
+  type: "file"
+  path: string
+}
+
+export type Range = {
+  start: {
+    line: number
+    character: number
+  }
+  end: {
+    line: number
+    character: number
+  }
+}
+
+export type SymbolSource = {
+  text: FilePartSourceText
+  type: "symbol"
+  path: string
+  range: Range
+  name: string
+  kind: number
+}
+
+export type FilePartSource = FileSource | SymbolSource
+
+export type FilePart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "file"
+  mime: string
+  filename?: string
+  url: string
+  source?: FilePartSource
+}
+
+export type ToolStatePending = {
+  status: "pending"
+  input: {
+    [key: string]: unknown
+  }
+  raw: string
+}
+
+export type ToolStateRunning = {
+  status: "running"
+  input: {
+    [key: string]: unknown
+  }
+  title?: string
+  metadata?: {
+    [key: string]: unknown
+  }
+  time: {
+    start: number
+  }
+}
+
+export type ToolStateCompleted = {
+  status: "completed"
+  input: {
+    [key: string]: unknown
+  }
+  output: string
+  title: string
+  metadata: {
+    [key: string]: unknown
+  }
+  time: {
+    start: number
+    end: number
+    compacted?: number
+  }
+  attachments?: Array<FilePart>
+}
+
+export type ToolStateError = {
+  status: "error"
+  input: {
+    [key: string]: unknown
+  }
+  error: string
+  metadata?: {
+    [key: string]: unknown
+  }
+  time: {
+    start: number
+    end: number
+  }
+}
+
+export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError
+
+export type ToolPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "tool"
+  callID: string
+  tool: string
+  state: ToolState
+  metadata?: {
+    [key: string]: unknown
+  }
+}
+
+export type StepStartPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "step-start"
+  snapshot?: string
+}
+
+export type StepFinishPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "step-finish"
+  reason: string
+  snapshot?: string
+  cost: number
+  tokens: {
+    input: number
+    output: number
+    reasoning: number
+    cache: {
+      read: number
+      write: number
+    }
+  }
+}
+
+export type SnapshotPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "snapshot"
+  snapshot: string
+}
+
+export type PatchPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "patch"
+  hash: string
+  files: Array<string>
+}
+
+export type AgentPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "agent"
+  name: string
+  source?: {
+    value: string
+    start: number
+    end: number
+  }
+}
+
+export type RetryPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "retry"
+  attempt: number
+  error: ApiError
+  time: {
+    created: number
+  }
+}
+
+export type CompactionPart = {
+  id: string
+  sessionID: string
+  messageID: string
+  type: "compaction"
+  auto: boolean
+}
+
+export type Part =
+  | TextPart
+  | {
+      id: string
+      sessionID: string
+      messageID: string
+      type: "subtask"
+      prompt: string
+      description: string
+      agent: string
+    }
+  | ReasoningPart
+  | FilePart
+  | ToolPart
+  | StepStartPart
+  | StepFinishPart
+  | SnapshotPart
+  | PatchPart
+  | AgentPart
+  | RetryPart
+  | CompactionPart
+
+export type EventMessagePartUpdated = {
+  type: "message.part.updated"
+  properties: {
+    part: Part
+    delta?: string
+  }
+}
+
+export type EventMessagePartRemoved = {
+  type: "message.part.removed"
+  properties: {
+    sessionID: string
+    messageID: string
+    partID: string
+  }
+}
+
+export type Permission = {
+  id: string
+  type: string
+  pattern?: string | Array<string>
+  sessionID: string
+  messageID: string
+  callID?: string
+  title: string
+  metadata: {
+    [key: string]: unknown
+  }
+  time: {
+    created: number
+  }
+}
+
+export type EventPermissionUpdated = {
+  type: "permission.updated"
+  properties: Permission
+}
+
+export type EventPermissionReplied = {
+  type: "permission.replied"
+  properties: {
+    sessionID: string
+    permissionID: string
+    response: string
+  }
+}
+
+export type SessionStatus =
+  | {
+      type: "idle"
+    }
+  | {
+      type: "retry"
+      attempt: number
+      message: string
+      next: number
+    }
+  | {
+      type: "busy"
+    }
+
+export type EventSessionStatus = {
+  type: "session.status"
+  properties: {
+    sessionID: string
+    status: SessionStatus
+  }
+}
+
+export type EventSessionIdle = {
+  type: "session.idle"
+  properties: {
+    sessionID: string
+  }
+}
+
+export type EventSessionCompacted = {
+  type: "session.compacted"
+  properties: {
+    sessionID: string
+  }
+}
+
+export type EventFileEdited = {
+  type: "file.edited"
+  properties: {
+    file: string
+  }
+}
+
+export type Todo = {
+  /**
+   * Brief description of the task
+   */
+  content: string
+  /**
+   * Current status of the task: pending, in_progress, completed, cancelled
+   */
+  status: string
+  /**
+   * Priority level of the task: high, medium, low
+   */
+  priority: string
+  /**
+   * Unique identifier for the todo item
+   */
+  id: string
+}
+
+export type EventTodoUpdated = {
+  type: "todo.updated"
+  properties: {
+    sessionID: string
+    todos: Array<Todo>
+  }
+}
+
+export type EventCommandExecuted = {
+  type: "command.executed"
+  properties: {
+    name: string
+    sessionID: string
+    arguments: string
+    messageID: string
+  }
+}
+
+export type Session = {
+  id: string
+  projectID: string
+  directory: string
+  parentID?: string
+  summary?: {
+    additions: number
+    deletions: number
+    files: number
+    diffs?: Array<FileDiff>
+  }
+  share?: {
+    url: string
+  }
+  title: string
+  version: string
+  time: {
+    created: number
+    updated: number
+    compacting?: number
+  }
+  revert?: {
+    messageID: string
+    partID?: string
+    snapshot?: string
+    diff?: string
+  }
+}
+
+export type EventSessionCreated = {
+  type: "session.created"
+  properties: {
+    info: Session
+  }
+}
+
+export type EventSessionUpdated = {
+  type: "session.updated"
+  properties: {
+    info: Session
+  }
+}
+
+export type EventSessionDeleted = {
+  type: "session.deleted"
+  properties: {
+    info: Session
+  }
+}
+
+export type EventSessionDiff = {
+  type: "session.diff"
+  properties: {
+    sessionID: string
+    diff: Array<FileDiff>
+  }
+}
+
+export type EventSessionError = {
+  type: "session.error"
+  properties: {
+    sessionID?: string
+    error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError
+  }
+}
+
+export type EventFileWatcherUpdated = {
+  type: "file.watcher.updated"
+  properties: {
+    file: string
+    event: "add" | "change" | "unlink"
+  }
+}
+
+export type EventVcsBranchUpdated = {
+  type: "vcs.branch.updated"
+  properties: {
+    branch?: string
+  }
+}
+
+export type EventTuiPromptAppend = {
+  type: "tui.prompt.append"
+  properties: {
+    text: string
+  }
+}
+
+export type EventTuiCommandExecute = {
+  type: "tui.command.execute"
+  properties: {
+    command:
+      | "session.list"
+      | "session.new"
+      | "session.share"
+      | "session.interrupt"
+      | "session.compact"
+      | "session.page.up"
+      | "session.page.down"
+      | "session.half.page.up"
+      | "session.half.page.down"
+      | "session.first"
+      | "session.last"
+      | "prompt.clear"
+      | "prompt.submit"
+      | "agent.cycle"
+      | string
+  }
+}
+
+export type EventTuiToastShow = {
+  type: "tui.toast.show"
+  properties: {
+    title?: string
+    message: string
+    variant: "info" | "success" | "warning" | "error"
+    /**
+     * Duration in milliseconds
+     */
+    duration?: number
+  }
+}
+
+export type Pty = {
+  id: string
+  title: string
+  command: string
+  args: Array<string>
+  cwd: string
+  status: "running" | "exited"
+  pid: number
+}
+
+export type EventPtyCreated = {
+  type: "pty.created"
+  properties: {
+    info: Pty
+  }
+}
+
+export type EventPtyUpdated = {
+  type: "pty.updated"
+  properties: {
+    info: Pty
+  }
+}
+
+export type EventPtyExited = {
+  type: "pty.exited"
+  properties: {
+    id: string
+    exitCode: number
+  }
+}
+
+export type EventPtyDeleted = {
+  type: "pty.deleted"
+  properties: {
+    id: string
+  }
+}
+
+export type EventServerConnected = {
+  type: "server.connected"
+  properties: {
+    [key: string]: unknown
+  }
+}
+
+export type Event =
+  | EventServerInstanceDisposed
+  | EventInstallationUpdated
+  | EventInstallationUpdateAvailable
+  | EventLspClientDiagnostics
+  | EventLspUpdated
+  | EventMessageUpdated
+  | EventMessageRemoved
+  | EventMessagePartUpdated
+  | EventMessagePartRemoved
+  | EventPermissionUpdated
+  | EventPermissionReplied
+  | EventSessionStatus
+  | EventSessionIdle
+  | EventSessionCompacted
+  | EventFileEdited
+  | EventTodoUpdated
+  | EventCommandExecuted
+  | EventSessionCreated
+  | EventSessionUpdated
+  | EventSessionDeleted
+  | EventSessionDiff
+  | EventSessionError
+  | EventFileWatcherUpdated
+  | EventVcsBranchUpdated
+  | EventTuiPromptAppend
+  | EventTuiCommandExecute
+  | EventTuiToastShow
+  | EventPtyCreated
+  | EventPtyUpdated
+  | EventPtyExited
+  | EventPtyDeleted
+  | EventServerConnected
+
+export type GlobalEvent = {
+  directory: string
+  payload: Event
+}
+
+export type Project = {
+  id: string
+  worktree: string
+  vcsDir?: string
+  vcs?: "git"
+  time: {
+    created: number
+    initialized?: number
+  }
+}
+
+export type BadRequestError = {
+  data: unknown
+  errors: Array<{
+    [key: string]: unknown
+  }>
+  success: false
+}
+
+export type NotFoundError = {
+  name: "NotFoundError"
+  data: {
+    message: string
+  }
+}
+
+/**
+ * Custom keybind configurations
+ */
+export type KeybindsConfig = {
+  /**
+   * Leader key for keybind combinations
+   */
+  leader?: string
+  /**
+   * Exit the application
+   */
+  app_exit?: string
+  /**
+   * Open external editor
+   */
+  editor_open?: string
+  /**
+   * List available themes
+   */
+  theme_list?: string
+  /**
+   * Toggle sidebar
+   */
+  sidebar_toggle?: string
+  /**
+   * Toggle session scrollbar
+   */
+  scrollbar_toggle?: string
+  /**
+   * Toggle username visibility
+   */
+  username_toggle?: string
+  /**
+   * View status
+   */
+  status_view?: string
+  /**
+   * Export session to editor
+   */
+  session_export?: string
+  /**
+   * Create a new session
+   */
+  session_new?: string
+  /**
+   * List all sessions
+   */
+  session_list?: string
+  /**
+   * Show session timeline
+   */
+  session_timeline?: string
+  /**
+   * Share current session
+   */
+  session_share?: string
+  /**
+   * Unshare current session
+   */
+  session_unshare?: string
+  /**
+   * Interrupt current session
+   */
+  session_interrupt?: string
+  /**
+   * Compact the session
+   */
+  session_compact?: string
+  /**
+   * Scroll messages up by one page
+   */
+  messages_page_up?: string
+  /**
+   * Scroll messages down by one page
+   */
+  messages_page_down?: string
+  /**
+   * Scroll messages up by half page
+   */
+  messages_half_page_up?: string
+  /**
+   * Scroll messages down by half page
+   */
+  messages_half_page_down?: string
+  /**
+   * Navigate to first message
+   */
+  messages_first?: string
+  /**
+   * Navigate to last message
+   */
+  messages_last?: string
+  /**
+   * Navigate to last user message
+   */
+  messages_last_user?: string
+  /**
+   * Copy message
+   */
+  messages_copy?: string
+  /**
+   * Undo message
+   */
+  messages_undo?: string
+  /**
+   * Redo message
+   */
+  messages_redo?: string
+  /**
+   * Toggle code block concealment in messages
+   */
+  messages_toggle_conceal?: string
+  /**
+   * Toggle tool details visibility
+   */
+  tool_details?: string
+  /**
+   * List available models
+   */
+  model_list?: string
+  /**
+   * Next recently used model
+   */
+  model_cycle_recent?: string
+  /**
+   * Previous recently used model
+   */
+  model_cycle_recent_reverse?: string
+  /**
+   * List available commands
+   */
+  command_list?: string
+  /**
+   * List agents
+   */
+  agent_list?: string
+  /**
+   * Next agent
+   */
+  agent_cycle?: string
+  /**
+   * Previous agent
+   */
+  agent_cycle_reverse?: string
+  /**
+   * Clear input field
+   */
+  input_clear?: string
+  /**
+   * Forward delete
+   */
+  input_forward_delete?: string
+  /**
+   * Paste from clipboard
+   */
+  input_paste?: string
+  /**
+   * Submit input
+   */
+  input_submit?: string
+  /**
+   * Insert newline in input
+   */
+  input_newline?: string
+  /**
+   * Previous history item
+   */
+  history_previous?: string
+  /**
+   * Next history item
+   */
+  history_next?: string
+  /**
+   * Next child session
+   */
+  session_child_cycle?: string
+  /**
+   * Previous child session
+   */
+  session_child_cycle_reverse?: string
+  /**
+   * Suspend terminal
+   */
+  terminal_suspend?: string
+}
+
+export type AgentConfig = {
+  model?: string
+  temperature?: number
+  top_p?: number
+  prompt?: string
+  tools?: {
+    [key: string]: boolean
+  }
+  disable?: boolean
+  /**
+   * Description of when to use the agent
+   */
+  description?: string
+  mode?: "subagent" | "primary" | "all"
+  /**
+   * Hex color code for the agent (e.g., #FF5733)
+   */
+  color?: string
+  /**
+   * Maximum number of agentic iterations before forcing text-only response
+   */
+  maxSteps?: number
+  permission?: {
+    edit?: "ask" | "allow" | "deny"
+    bash?:
+      | "ask"
+      | "allow"
+      | "deny"
+      | {
+          [key: string]: "ask" | "allow" | "deny"
+        }
+    webfetch?: "ask" | "allow" | "deny"
+    doom_loop?: "ask" | "allow" | "deny"
+    external_directory?: "ask" | "allow" | "deny"
+  }
+  [key: string]:
+    | unknown
+    | string
+    | number
+    | {
+        [key: string]: boolean
+      }
+    | boolean
+    | "subagent"
+    | "primary"
+    | "all"
+    | string
+    | number
+    | {
+        edit?: "ask" | "allow" | "deny"
+        bash?:
+          | "ask"
+          | "allow"
+          | "deny"
+          | {
+              [key: string]: "ask" | "allow" | "deny"
+            }
+        webfetch?: "ask" | "allow" | "deny"
+        doom_loop?: "ask" | "allow" | "deny"
+        external_directory?: "ask" | "allow" | "deny"
+      }
+    | undefined
+}
+
+export type ProviderConfig = {
+  api?: string
+  name?: string
+  env?: Array<string>
+  id?: string
+  npm?: string
+  models?: {
+    [key: string]: {
+      id?: string
+      name?: string
+      release_date?: string
+      attachment?: boolean
+      reasoning?: boolean
+      temperature?: boolean
+      tool_call?: boolean
+      cost?: {
+        input: number
+        output: number
+        cache_read?: number
+        cache_write?: number
+        context_over_200k?: {
+          input: number
+          output: number
+          cache_read?: number
+          cache_write?: number
+        }
+      }
+      limit?: {
+        context: number
+        output: number
+      }
+      modalities?: {
+        input: Array<"text" | "audio" | "image" | "video" | "pdf">
+        output: Array<"text" | "audio" | "image" | "video" | "pdf">
+      }
+      experimental?: boolean
+      status?: "alpha" | "beta" | "deprecated"
+      options?: {
+        [key: string]: unknown
+      }
+      headers?: {
+        [key: string]: string
+      }
+      provider?: {
+        npm: string
+      }
+    }
+  }
+  whitelist?: Array<string>
+  blacklist?: Array<string>
+  options?: {
+    apiKey?: string
+    baseURL?: string
+    /**
+     * GitHub Enterprise URL for copilot authentication
+     */
+    enterpriseUrl?: string
+    /**
+     * Enable promptCacheKey for this provider (default false)
+     */
+    setCacheKey?: boolean
+    /**
+     * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
+     */
+    timeout?: number | false
+    [key: string]: unknown | string | boolean | number | false | undefined
+  }
+}
+
+export type McpLocalConfig = {
+  /**
+   * Type of MCP server connection
+   */
+  type: "local"
+  /**
+   * Command and arguments to run the MCP server
+   */
+  command: Array<string>
+  /**
+   * Environment variables to set when running the MCP server
+   */
+  environment?: {
+    [key: string]: string
+  }
+  /**
+   * Enable or disable the MCP server on startup
+   */
+  enabled?: boolean
+  /**
+   * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified.
+   */
+  timeout?: number
+}
+
+export type McpOAuthConfig = {
+  /**
+   * OAuth client ID. If not provided, dynamic client registration (RFC 7591) will be attempted.
+   */
+  clientId?: string
+  /**
+   * OAuth client secret (if required by the authorization server)
+   */
+  clientSecret?: string
+  /**
+   * OAuth scopes to request during authorization
+   */
+  scope?: string
+}
+
+export type McpRemoteConfig = {
+  /**
+   * Type of MCP server connection
+   */
+  type: "remote"
+  /**
+   * URL of the remote MCP server
+   */
+  url: string
+  /**
+   * Enable or disable the MCP server on startup
+   */
+  enabled?: boolean
+  /**
+   * Headers to send with the request
+   */
+  headers?: {
+    [key: string]: string
+  }
+  /**
+   * OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection.
+   */
+  oauth?: McpOAuthConfig | false
+  /**
+   * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified.
+   */
+  timeout?: number
+}
+
+/**
+ * @deprecated Always uses stretch layout.
+ */
+export type LayoutConfig = "auto" | "stretch"
+
+export type Config = {
+  /**
+   * JSON schema reference for configuration validation
+   */
+  $schema?: string
+  /**
+   * Theme name to use for the interface
+   */
+  theme?: string
+  keybinds?: KeybindsConfig
+  /**
+   * TUI specific settings
+   */
+  tui?: {
+    /**
+     * TUI scroll speed
+     */
+    scroll_speed?: number
+    /**
+     * Scroll acceleration settings
+     */
+    scroll_acceleration?: {
+      /**
+       * Enable scroll acceleration
+       */
+      enabled: boolean
+    }
+    /**
+     * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column
+     */
+    diff_style?: "auto" | "stacked"
+  }
+  /**
+   * Command configuration, see https://opencode.ai/docs/commands
+   */
+  command?: {
+    [key: string]: {
+      template: string
+      description?: string
+      agent?: string
+      model?: string
+      subtask?: boolean
+    }
+  }
+  watcher?: {
+    ignore?: Array<string>
+  }
+  plugin?: Array<string>
+  snapshot?: boolean
+  /**
+   * Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing
+   */
+  share?: "manual" | "auto" | "disabled"
+  /**
+   * @deprecated Use 'share' field instead. Share newly created sessions automatically
+   */
+  autoshare?: boolean
+  /**
+   * Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications
+   */
+  autoupdate?: boolean | "notify"
+  /**
+   * Disable providers that are loaded automatically
+   */
+  disabled_providers?: Array<string>
+  /**
+   * When set, ONLY these providers will be enabled. All other providers will be ignored
+   */
+  enabled_providers?: Array<string>
+  /**
+   * Model to use in the format of provider/model, eg anthropic/claude-2
+   */
+  model?: string
+  /**
+   * Small model to use for tasks like title generation in the format of provider/model
+   */
+  small_model?: string
+  /**
+   * Custom username to display in conversations instead of system username
+   */
+  username?: string
+  /**
+   * @deprecated Use `agent` field instead.
+   */
+  mode?: {
+    build?: AgentConfig
+    plan?: AgentConfig
+    [key: string]: AgentConfig | undefined
+  }
+  /**
+   * Agent configuration, see https://opencode.ai/docs/agent
+   */
+  agent?: {
+    plan?: AgentConfig
+    build?: AgentConfig
+    general?: AgentConfig
+    explore?: AgentConfig
+    [key: string]: AgentConfig | undefined
+  }
+  /**
+   * Custom provider configurations and model overrides
+   */
+  provider?: {
+    [key: string]: ProviderConfig
+  }
+  /**
+   * MCP (Model Context Protocol) server configurations
+   */
+  mcp?: {
+    [key: string]: McpLocalConfig | McpRemoteConfig
+  }
+  formatter?:
+    | false
+    | {
+        [key: string]: {
+          disabled?: boolean
+          command?: Array<string>
+          environment?: {
+            [key: string]: string
+          }
+          extensions?: Array<string>
+        }
+      }
+  lsp?:
+    | false
+    | {
+        [key: string]:
+          | {
+              disabled: true
+            }
+          | {
+              command: Array<string>
+              extensions?: Array<string>
+              disabled?: boolean
+              env?: {
+                [key: string]: string
+              }
+              initialization?: {
+                [key: string]: unknown
+              }
+            }
+      }
+  /**
+   * Additional instruction files or patterns to include
+   */
+  instructions?: Array<string>
+  layout?: LayoutConfig
+  permission?: {
+    edit?: "ask" | "allow" | "deny"
+    bash?:
+      | "ask"
+      | "allow"
+      | "deny"
+      | {
+          [key: string]: "ask" | "allow" | "deny"
+        }
+    webfetch?: "ask" | "allow" | "deny"
+    doom_loop?: "ask" | "allow" | "deny"
+    external_directory?: "ask" | "allow" | "deny"
+  }
+  tools?: {
+    [key: string]: boolean
+  }
+  enterprise?: {
+    /**
+     * Enterprise URL
+     */
+    url?: string
+  }
+  experimental?: {
+    hook?: {
+      file_edited?: {
+        [key: string]: Array<{
+          command: Array<string>
+          environment?: {
+            [key: string]: string
+          }
+        }>
+      }
+      session_completed?: Array<{
+        command: Array<string>
+        environment?: {
+          [key: string]: string
+        }
+      }>
+    }
+    /**
+     * Number of retries for chat completions on failure
+     */
+    chatMaxRetries?: number
+    disable_paste_summary?: boolean
+    /**
+     * Enable the batch tool
+     */
+    batch_tool?: boolean
+    /**
+     * Enable OpenTelemetry spans for AI SDK calls (using the 'experimental_telemetry' flag)
+     */
+    openTelemetry?: boolean
+    /**
+     * Tools that should only be available to primary agents.
+     */
+    primary_tools?: Array<string>
+  }
+}
+
+export type ToolIds = Array<string>
+
+export type ToolListItem = {
+  id: string
+  description: string
+  parameters: unknown
+}
+
+export type ToolList = Array<ToolListItem>
+
+export type Path = {
+  state: string
+  config: string
+  worktree: string
+  directory: string
+}
+
+export type VcsInfo = {
+  branch: string
+}
+
+export type TextPartInput = {
+  id?: string
+  type: "text"
+  text: string
+  synthetic?: boolean
+  ignored?: boolean
+  time?: {
+    start: number
+    end?: number
+  }
+  metadata?: {
+    [key: string]: unknown
+  }
+}
+
+export type FilePartInput = {
+  id?: string
+  type: "file"
+  mime: string
+  filename?: string
+  url: string
+  source?: FilePartSource
+}
+
+export type AgentPartInput = {
+  id?: string
+  type: "agent"
+  name: string
+  source?: {
+    value: string
+    start: number
+    end: number
+  }
+}
+
+export type SubtaskPartInput = {
+  id?: string
+  type: "subtask"
+  prompt: string
+  description: string
+  agent: string
+}
+
+export type Command = {
+  name: string
+  description?: string
+  agent?: string
+  model?: string
+  template: string
+  subtask?: boolean
+}
+
+export type Model = {
+  id: string
+  providerID: string
+  api: {
+    id: string
+    url: string
+    npm: string
+  }
+  name: string
+  capabilities: {
+    temperature: boolean
+    reasoning: boolean
+    attachment: boolean
+    toolcall: boolean
+    input: {
+      text: boolean
+      audio: boolean
+      image: boolean
+      video: boolean
+      pdf: boolean
+    }
+    output: {
+      text: boolean
+      audio: boolean
+      image: boolean
+      video: boolean
+      pdf: boolean
+    }
+  }
+  cost: {
+    input: number
+    output: number
+    cache: {
+      read: number
+      write: number
+    }
+    experimentalOver200K?: {
+      input: number
+      output: number
+      cache: {
+        read: number
+        write: number
+      }
+    }
+  }
+  limit: {
+    context: number
+    output: number
+  }
+  status: "alpha" | "beta" | "deprecated" | "active"
+  options: {
+    [key: string]: unknown
+  }
+  headers: {
+    [key: string]: string
+  }
+}
+
+export type Provider = {
+  id: string
+  name: string
+  source: "env" | "config" | "custom" | "api"
+  env: Array<string>
+  key?: string
+  options: {
+    [key: string]: unknown
+  }
+  models: {
+    [key: string]: Model
+  }
+}
+
+export type ProviderAuthMethod = {
+  type: "oauth" | "api"
+  label: string
+}
+
+export type ProviderAuthAuthorization = {
+  url: string
+  method: "auto" | "code"
+  instructions: string
+}
+
+export type Symbol = {
+  name: string
+  kind: number
+  location: {
+    uri: string
+    range: Range
+  }
+}
+
+export type FileNode = {
+  name: string
+  path: string
+  absolute: string
+  type: "file" | "directory"
+  ignored: boolean
+}
+
+export type FileContent = {
+  type: "text"
+  content: string
+  diff?: string
+  patch?: {
+    oldFileName: string
+    newFileName: string
+    oldHeader?: string
+    newHeader?: string
+    hunks: Array<{
+      oldStart: number
+      oldLines: number
+      newStart: number
+      newLines: number
+      lines: Array<string>
+    }>
+    index?: string
+  }
+  encoding?: "base64"
+  mimeType?: string
+}
+
+export type File = {
+  path: string
+  added: number
+  removed: number
+  status: "added" | "deleted" | "modified"
+}
+
+export type Agent = {
+  name: string
+  description?: string
+  mode: "subagent" | "primary" | "all"
+  builtIn: boolean
+  topP?: number
+  temperature?: number
+  color?: string
+  permission: {
+    edit: "ask" | "allow" | "deny"
+    bash: {
+      [key: string]: "ask" | "allow" | "deny"
+    }
+    webfetch?: "ask" | "allow" | "deny"
+    doom_loop?: "ask" | "allow" | "deny"
+    external_directory?: "ask" | "allow" | "deny"
+  }
+  model?: {
+    modelID: string
+    providerID: string
+  }
+  prompt?: string
+  tools: {
+    [key: string]: boolean
+  }
+  options: {
+    [key: string]: unknown
+  }
+  maxSteps?: number
+}
+
+export type McpStatusConnected = {
+  status: "connected"
+}
+
+export type McpStatusDisabled = {
+  status: "disabled"
+}
+
+export type McpStatusFailed = {
+  status: "failed"
+  error: string
+}
+
+export type McpStatusNeedsAuth = {
+  status: "needs_auth"
+}
+
+export type McpStatusNeedsClientRegistration = {
+  status: "needs_client_registration"
+  error: string
+}
+
+export type McpStatus =
+  | McpStatusConnected
+  | McpStatusDisabled
+  | McpStatusFailed
+  | McpStatusNeedsAuth
+  | McpStatusNeedsClientRegistration
+
+export type LspStatus = {
+  id: string
+  name: string
+  root: string
+  status: "connected" | "error"
+}
+
+export type FormatterStatus = {
+  name: string
+  extensions: Array<string>
+  enabled: boolean
+}
+
+export type OAuth = {
+  type: "oauth"
+  refresh: string
+  access: string
+  expires: number
+  enterpriseUrl?: string
+}
+
+export type ApiAuth = {
+  type: "api"
+  key: string
+}
+
+export type WellKnownAuth = {
+  type: "wellknown"
+  key: string
+  token: string
+}
+
+export type Auth = OAuth | ApiAuth | WellKnownAuth
+
+export type GlobalEventData = {
+  body?: never
+  path?: never
+  query?: never
+  url: "/global/event"
+}
+
+export type GlobalEventResponses = {
+  /**
+   * Event stream
+   */
+  200: GlobalEvent
+}
+
+export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
+
+export type ProjectListData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/project"
+}
+
+export type ProjectListResponses = {
+  /**
+   * List of projects
+   */
+  200: Array<Project>
+}
+
+export type ProjectListResponse = ProjectListResponses[keyof ProjectListResponses]
+
+export type ProjectCurrentData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/project/current"
+}
+
+export type ProjectCurrentResponses = {
+  /**
+   * Current project
+   */
+  200: Project
+}
+
+export type ProjectCurrentResponse = ProjectCurrentResponses[keyof ProjectCurrentResponses]
+
+export type PtyListData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/pty"
+}
+
+export type PtyListResponses = {
+  /**
+   * List of sessions
+   */
+  200: Array<Pty>
+}
+
+export type PtyListResponse = PtyListResponses[keyof PtyListResponses]
+
+export type PtyCreateData = {
+  body?: {
+    command?: string
+    args?: Array<string>
+    cwd?: string
+    title?: string
+    env?: {
+      [key: string]: string
+    }
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/pty"
+}
+
+export type PtyCreateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type PtyCreateError = PtyCreateErrors[keyof PtyCreateErrors]
+
+export type PtyCreateResponses = {
+  /**
+   * Created session
+   */
+  200: Pty
+}
+
+export type PtyCreateResponse = PtyCreateResponses[keyof PtyCreateResponses]
+
+export type PtyRemoveData = {
+  body?: never
+  path: {
+    ptyID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/pty/{ptyID}"
+}
+
+export type PtyRemoveErrors = {
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type PtyRemoveError = PtyRemoveErrors[keyof PtyRemoveErrors]
+
+export type PtyRemoveResponses = {
+  /**
+   * Session removed
+   */
+  200: boolean
+}
+
+export type PtyRemoveResponse = PtyRemoveResponses[keyof PtyRemoveResponses]
+
+export type PtyGetData = {
+  body?: never
+  path: {
+    ptyID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/pty/{ptyID}"
+}
+
+export type PtyGetErrors = {
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type PtyGetError = PtyGetErrors[keyof PtyGetErrors]
+
+export type PtyGetResponses = {
+  /**
+   * Session info
+   */
+  200: Pty
+}
+
+export type PtyGetResponse = PtyGetResponses[keyof PtyGetResponses]
+
+export type PtyUpdateData = {
+  body?: {
+    title?: string
+    size?: {
+      rows: number
+      cols: number
+    }
+  }
+  path: {
+    ptyID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/pty/{ptyID}"
+}
+
+export type PtyUpdateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type PtyUpdateError = PtyUpdateErrors[keyof PtyUpdateErrors]
+
+export type PtyUpdateResponses = {
+  /**
+   * Updated session
+   */
+  200: Pty
+}
+
+export type PtyUpdateResponse = PtyUpdateResponses[keyof PtyUpdateResponses]
+
+export type PtyConnectData = {
+  body?: never
+  path: {
+    ptyID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/pty/{ptyID}/connect"
+}
+
+export type PtyConnectErrors = {
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type PtyConnectError = PtyConnectErrors[keyof PtyConnectErrors]
+
+export type PtyConnectResponses = {
+  /**
+   * Connected session
+   */
+  200: boolean
+}
+
+export type PtyConnectResponse = PtyConnectResponses[keyof PtyConnectResponses]
+
+export type ConfigGetData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/config"
+}
+
+export type ConfigGetResponses = {
+  /**
+   * Get config info
+   */
+  200: Config
+}
+
+export type ConfigGetResponse = ConfigGetResponses[keyof ConfigGetResponses]
+
+export type ConfigUpdateData = {
+  body?: Config
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/config"
+}
+
+export type ConfigUpdateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type ConfigUpdateError = ConfigUpdateErrors[keyof ConfigUpdateErrors]
+
+export type ConfigUpdateResponses = {
+  /**
+   * Successfully updated config
+   */
+  200: Config
+}
+
+export type ConfigUpdateResponse = ConfigUpdateResponses[keyof ConfigUpdateResponses]
+
+export type ToolIdsData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/experimental/tool/ids"
+}
+
+export type ToolIdsErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type ToolIdsError = ToolIdsErrors[keyof ToolIdsErrors]
+
+export type ToolIdsResponses = {
+  /**
+   * Tool IDs
+   */
+  200: ToolIds
+}
+
+export type ToolIdsResponse = ToolIdsResponses[keyof ToolIdsResponses]
+
+export type ToolListData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    provider: string
+    model: string
+  }
+  url: "/experimental/tool"
+}
+
+export type ToolListErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type ToolListError = ToolListErrors[keyof ToolListErrors]
+
+export type ToolListResponses = {
+  /**
+   * Tools
+   */
+  200: ToolList
+}
+
+export type ToolListResponse = ToolListResponses[keyof ToolListResponses]
+
+export type InstanceDisposeData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/instance/dispose"
+}
+
+export type InstanceDisposeResponses = {
+  /**
+   * Instance disposed
+   */
+  200: boolean
+}
+
+export type InstanceDisposeResponse = InstanceDisposeResponses[keyof InstanceDisposeResponses]
+
+export type PathGetData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/path"
+}
+
+export type PathGetResponses = {
+  /**
+   * Path
+   */
+  200: Path
+}
+
+export type PathGetResponse = PathGetResponses[keyof PathGetResponses]
+
+export type VcsGetData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/vcs"
+}
+
+export type VcsGetResponses = {
+  /**
+   * VCS info
+   */
+  200: VcsInfo
+}
+
+export type VcsGetResponse = VcsGetResponses[keyof VcsGetResponses]
+
+export type SessionListData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/session"
+}
+
+export type SessionListResponses = {
+  /**
+   * List of sessions
+   */
+  200: Array<Session>
+}
+
+export type SessionListResponse = SessionListResponses[keyof SessionListResponses]
+
+export type SessionCreateData = {
+  body?: {
+    parentID?: string
+    title?: string
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/session"
+}
+
+export type SessionCreateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type SessionCreateError = SessionCreateErrors[keyof SessionCreateErrors]
+
+export type SessionCreateResponses = {
+  /**
+   * Successfully created session
+   */
+  200: Session
+}
+
+export type SessionCreateResponse = SessionCreateResponses[keyof SessionCreateResponses]
+
+export type SessionStatusData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/session/status"
+}
+
+export type SessionStatusErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type SessionStatusError = SessionStatusErrors[keyof SessionStatusErrors]
+
+export type SessionStatusResponses = {
+  /**
+   * Get session status
+   */
+  200: {
+    [key: string]: SessionStatus
+  }
+}
+
+export type SessionStatusResponse = SessionStatusResponses[keyof SessionStatusResponses]
+
+export type SessionDeleteData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}"
+}
+
+export type SessionDeleteErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionDeleteError = SessionDeleteErrors[keyof SessionDeleteErrors]
+
+export type SessionDeleteResponses = {
+  /**
+   * Successfully deleted session
+   */
+  200: boolean
+}
+
+export type SessionDeleteResponse = SessionDeleteResponses[keyof SessionDeleteResponses]
+
+export type SessionGetData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}"
+}
+
+export type SessionGetErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionGetError = SessionGetErrors[keyof SessionGetErrors]
+
+export type SessionGetResponses = {
+  /**
+   * Get session
+   */
+  200: Session
+}
+
+export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses]
+
+export type SessionUpdateData = {
+  body?: {
+    title?: string
+  }
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}"
+}
+
+export type SessionUpdateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionUpdateError = SessionUpdateErrors[keyof SessionUpdateErrors]
+
+export type SessionUpdateResponses = {
+  /**
+   * Successfully updated session
+   */
+  200: Session
+}
+
+export type SessionUpdateResponse = SessionUpdateResponses[keyof SessionUpdateResponses]
+
+export type SessionChildrenData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/children"
+}
+
+export type SessionChildrenErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionChildrenError = SessionChildrenErrors[keyof SessionChildrenErrors]
+
+export type SessionChildrenResponses = {
+  /**
+   * List of children
+   */
+  200: Array<Session>
+}
+
+export type SessionChildrenResponse = SessionChildrenResponses[keyof SessionChildrenResponses]
+
+export type SessionTodoData = {
+  body?: never
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/todo"
+}
+
+export type SessionTodoErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionTodoError = SessionTodoErrors[keyof SessionTodoErrors]
+
+export type SessionTodoResponses = {
+  /**
+   * Todo list
+   */
+  200: Array<Todo>
+}
+
+export type SessionTodoResponse = SessionTodoResponses[keyof SessionTodoResponses]
+
+export type SessionInitData = {
+  body?: {
+    modelID: string
+    providerID: string
+    messageID: string
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/init"
+}
+
+export type SessionInitErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionInitError = SessionInitErrors[keyof SessionInitErrors]
+
+export type SessionInitResponses = {
+  /**
+   * 200
+   */
+  200: boolean
+}
+
+export type SessionInitResponse = SessionInitResponses[keyof SessionInitResponses]
+
+export type SessionForkData = {
+  body?: {
+    messageID?: string
+  }
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/fork"
+}
+
+export type SessionForkResponses = {
+  /**
+   * 200
+   */
+  200: Session
+}
+
+export type SessionForkResponse = SessionForkResponses[keyof SessionForkResponses]
+
+export type SessionAbortData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/abort"
+}
+
+export type SessionAbortErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionAbortError = SessionAbortErrors[keyof SessionAbortErrors]
+
+export type SessionAbortResponses = {
+  /**
+   * Aborted session
+   */
+  200: boolean
+}
+
+export type SessionAbortResponse = SessionAbortResponses[keyof SessionAbortResponses]
+
+export type SessionUnshareData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/share"
+}
+
+export type SessionUnshareErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionUnshareError = SessionUnshareErrors[keyof SessionUnshareErrors]
+
+export type SessionUnshareResponses = {
+  /**
+   * Successfully unshared session
+   */
+  200: Session
+}
+
+export type SessionUnshareResponse = SessionUnshareResponses[keyof SessionUnshareResponses]
+
+export type SessionShareData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/share"
+}
+
+export type SessionShareErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionShareError = SessionShareErrors[keyof SessionShareErrors]
+
+export type SessionShareResponses = {
+  /**
+   * Successfully shared session
+   */
+  200: Session
+}
+
+export type SessionShareResponse = SessionShareResponses[keyof SessionShareResponses]
+
+export type SessionDiffData = {
+  body?: never
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+    messageID?: string
+  }
+  url: "/session/{sessionID}/diff"
+}
+
+export type SessionDiffErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionDiffError = SessionDiffErrors[keyof SessionDiffErrors]
+
+export type SessionDiffResponses = {
+  /**
+   * List of diffs
+   */
+  200: Array<FileDiff>
+}
+
+export type SessionDiffResponse = SessionDiffResponses[keyof SessionDiffResponses]
+
+export type SessionSummarizeData = {
+  body?: {
+    providerID: string
+    modelID: string
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/summarize"
+}
+
+export type SessionSummarizeErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionSummarizeError = SessionSummarizeErrors[keyof SessionSummarizeErrors]
+
+export type SessionSummarizeResponses = {
+  /**
+   * Summarized session
+   */
+  200: boolean
+}
+
+export type SessionSummarizeResponse = SessionSummarizeResponses[keyof SessionSummarizeResponses]
+
+export type SessionMessagesData = {
+  body?: never
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+    limit?: number
+  }
+  url: "/session/{sessionID}/message"
+}
+
+export type SessionMessagesErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionMessagesError = SessionMessagesErrors[keyof SessionMessagesErrors]
+
+export type SessionMessagesResponses = {
+  /**
+   * List of messages
+   */
+  200: Array<{
+    info: Message
+    parts: Array<Part>
+  }>
+}
+
+export type SessionMessagesResponse = SessionMessagesResponses[keyof SessionMessagesResponses]
+
+export type SessionPromptData = {
+  body?: {
+    messageID?: string
+    model?: {
+      providerID: string
+      modelID: string
+    }
+    agent?: string
+    noReply?: boolean
+    system?: string
+    tools?: {
+      [key: string]: boolean
+    }
+    parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/message"
+}
+
+export type SessionPromptErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionPromptError = SessionPromptErrors[keyof SessionPromptErrors]
+
+export type SessionPromptResponses = {
+  /**
+   * Created message
+   */
+  200: {
+    info: AssistantMessage
+    parts: Array<Part>
+  }
+}
+
+export type SessionPromptResponse = SessionPromptResponses[keyof SessionPromptResponses]
+
+export type SessionMessageData = {
+  body?: never
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+    /**
+     * Message ID
+     */
+    messageID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/message/{messageID}"
+}
+
+export type SessionMessageErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionMessageError = SessionMessageErrors[keyof SessionMessageErrors]
+
+export type SessionMessageResponses = {
+  /**
+   * Message
+   */
+  200: {
+    info: Message
+    parts: Array<Part>
+  }
+}
+
+export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses]
+
+export type SessionPromptAsyncData = {
+  body?: {
+    messageID?: string
+    model?: {
+      providerID: string
+      modelID: string
+    }
+    agent?: string
+    noReply?: boolean
+    system?: string
+    tools?: {
+      [key: string]: boolean
+    }
+    parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/prompt_async"
+}
+
+export type SessionPromptAsyncErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionPromptAsyncError = SessionPromptAsyncErrors[keyof SessionPromptAsyncErrors]
+
+export type SessionPromptAsyncResponses = {
+  /**
+   * Prompt accepted
+   */
+  204: void
+}
+
+export type SessionPromptAsyncResponse = SessionPromptAsyncResponses[keyof SessionPromptAsyncResponses]
+
+export type SessionCommandData = {
+  body?: {
+    messageID?: string
+    agent?: string
+    model?: string
+    arguments: string
+    command: string
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/command"
+}
+
+export type SessionCommandErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionCommandError = SessionCommandErrors[keyof SessionCommandErrors]
+
+export type SessionCommandResponses = {
+  /**
+   * Created message
+   */
+  200: {
+    info: AssistantMessage
+    parts: Array<Part>
+  }
+}
+
+export type SessionCommandResponse = SessionCommandResponses[keyof SessionCommandResponses]
+
+export type SessionShellData = {
+  body?: {
+    agent: string
+    model?: {
+      providerID: string
+      modelID: string
+    }
+    command: string
+  }
+  path: {
+    /**
+     * Session ID
+     */
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/shell"
+}
+
+export type SessionShellErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionShellError = SessionShellErrors[keyof SessionShellErrors]
+
+export type SessionShellResponses = {
+  /**
+   * Created message
+   */
+  200: AssistantMessage
+}
+
+export type SessionShellResponse = SessionShellResponses[keyof SessionShellResponses]
+
+export type SessionRevertData = {
+  body?: {
+    messageID: string
+    partID?: string
+  }
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/revert"
+}
+
+export type SessionRevertErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionRevertError = SessionRevertErrors[keyof SessionRevertErrors]
+
+export type SessionRevertResponses = {
+  /**
+   * Updated session
+   */
+  200: Session
+}
+
+export type SessionRevertResponse = SessionRevertResponses[keyof SessionRevertResponses]
+
+export type SessionUnrevertData = {
+  body?: never
+  path: {
+    sessionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/unrevert"
+}
+
+export type SessionUnrevertErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type SessionUnrevertError = SessionUnrevertErrors[keyof SessionUnrevertErrors]
+
+export type SessionUnrevertResponses = {
+  /**
+   * Updated session
+   */
+  200: Session
+}
+
+export type SessionUnrevertResponse = SessionUnrevertResponses[keyof SessionUnrevertResponses]
+
+export type PermissionRespondData = {
+  body?: {
+    response: "once" | "always" | "reject"
+  }
+  path: {
+    sessionID: string
+    permissionID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/session/{sessionID}/permissions/{permissionID}"
+}
+
+export type PermissionRespondErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type PermissionRespondError = PermissionRespondErrors[keyof PermissionRespondErrors]
+
+export type PermissionRespondResponses = {
+  /**
+   * Permission processed successfully
+   */
+  200: boolean
+}
+
+export type PermissionRespondResponse = PermissionRespondResponses[keyof PermissionRespondResponses]
+
+export type CommandListData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/command"
+}
+
+export type CommandListResponses = {
+  /**
+   * List of commands
+   */
+  200: Array<Command>
+}
+
+export type CommandListResponse = CommandListResponses[keyof CommandListResponses]
+
+export type ConfigProvidersData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/config/providers"
+}
+
+export type ConfigProvidersResponses = {
+  /**
+   * List of providers
+   */
+  200: {
+    providers: Array<Provider>
+    default: {
+      [key: string]: string
+    }
+  }
+}
+
+export type ConfigProvidersResponse = ConfigProvidersResponses[keyof ConfigProvidersResponses]
+
+export type ProviderListData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/provider"
+}
+
+export type ProviderListResponses = {
+  /**
+   * List of providers
+   */
+  200: {
+    all: Array<{
+      api?: string
+      name: string
+      env: Array<string>
+      id: string
+      npm?: string
+      models: {
+        [key: string]: {
+          id: string
+          name: string
+          release_date: string
+          attachment: boolean
+          reasoning: boolean
+          temperature: boolean
+          tool_call: boolean
+          cost?: {
+            input: number
+            output: number
+            cache_read?: number
+            cache_write?: number
+            context_over_200k?: {
+              input: number
+              output: number
+              cache_read?: number
+              cache_write?: number
+            }
+          }
+          limit: {
+            context: number
+            output: number
+          }
+          modalities?: {
+            input: Array<"text" | "audio" | "image" | "video" | "pdf">
+            output: Array<"text" | "audio" | "image" | "video" | "pdf">
+          }
+          experimental?: boolean
+          status?: "alpha" | "beta" | "deprecated"
+          options: {
+            [key: string]: unknown
+          }
+          headers?: {
+            [key: string]: string
+          }
+          provider?: {
+            npm: string
+          }
+        }
+      }
+    }>
+    default: {
+      [key: string]: string
+    }
+    connected: Array<string>
+  }
+}
+
+export type ProviderListResponse = ProviderListResponses[keyof ProviderListResponses]
+
+export type ProviderAuthData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/provider/auth"
+}
+
+export type ProviderAuthResponses = {
+  /**
+   * Provider auth methods
+   */
+  200: {
+    [key: string]: Array<ProviderAuthMethod>
+  }
+}
+
+export type ProviderAuthResponse = ProviderAuthResponses[keyof ProviderAuthResponses]
+
+export type ProviderOauthAuthorizeData = {
+  body?: {
+    /**
+     * Auth method index
+     */
+    method: number
+  }
+  path: {
+    /**
+     * Provider ID
+     */
+    providerID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/provider/{providerID}/oauth/authorize"
+}
+
+export type ProviderOauthAuthorizeErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type ProviderOauthAuthorizeError = ProviderOauthAuthorizeErrors[keyof ProviderOauthAuthorizeErrors]
+
+export type ProviderOauthAuthorizeResponses = {
+  /**
+   * Authorization URL and method
+   */
+  200: ProviderAuthAuthorization
+}
+
+export type ProviderOauthAuthorizeResponse = ProviderOauthAuthorizeResponses[keyof ProviderOauthAuthorizeResponses]
+
+export type ProviderOauthCallbackData = {
+  body?: {
+    /**
+     * Auth method index
+     */
+    method: number
+    /**
+     * OAuth authorization code
+     */
+    code?: string
+  }
+  path: {
+    /**
+     * Provider ID
+     */
+    providerID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/provider/{providerID}/oauth/callback"
+}
+
+export type ProviderOauthCallbackErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type ProviderOauthCallbackError = ProviderOauthCallbackErrors[keyof ProviderOauthCallbackErrors]
+
+export type ProviderOauthCallbackResponses = {
+  /**
+   * OAuth callback processed successfully
+   */
+  200: boolean
+}
+
+export type ProviderOauthCallbackResponse = ProviderOauthCallbackResponses[keyof ProviderOauthCallbackResponses]
+
+export type FindTextData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    pattern: string
+  }
+  url: "/find"
+}
+
+export type FindTextResponses = {
+  /**
+   * Matches
+   */
+  200: Array<{
+    path: {
+      text: string
+    }
+    lines: {
+      text: string
+    }
+    line_number: number
+    absolute_offset: number
+    submatches: Array<{
+      match: {
+        text: string
+      }
+      start: number
+      end: number
+    }>
+  }>
+}
+
+export type FindTextResponse = FindTextResponses[keyof FindTextResponses]
+
+export type FindFilesData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    query: string
+    dirs?: "true" | "false"
+  }
+  url: "/find/file"
+}
+
+export type FindFilesResponses = {
+  /**
+   * File paths
+   */
+  200: Array<string>
+}
+
+export type FindFilesResponse = FindFilesResponses[keyof FindFilesResponses]
+
+export type FindSymbolsData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    query: string
+  }
+  url: "/find/symbol"
+}
+
+export type FindSymbolsResponses = {
+  /**
+   * Symbols
+   */
+  200: Array<Symbol>
+}
+
+export type FindSymbolsResponse = FindSymbolsResponses[keyof FindSymbolsResponses]
+
+export type FileListData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    path: string
+  }
+  url: "/file"
+}
+
+export type FileListResponses = {
+  /**
+   * Files and directories
+   */
+  200: Array<FileNode>
+}
+
+export type FileListResponse = FileListResponses[keyof FileListResponses]
+
+export type FileReadData = {
+  body?: never
+  path?: never
+  query: {
+    directory?: string
+    path: string
+  }
+  url: "/file/content"
+}
+
+export type FileReadResponses = {
+  /**
+   * File content
+   */
+  200: FileContent
+}
+
+export type FileReadResponse = FileReadResponses[keyof FileReadResponses]
+
+export type FileStatusData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/file/status"
+}
+
+export type FileStatusResponses = {
+  /**
+   * File status
+   */
+  200: Array<File>
+}
+
+export type FileStatusResponse = FileStatusResponses[keyof FileStatusResponses]
+
+export type AppLogData = {
+  body?: {
+    /**
+     * Service name for the log entry
+     */
+    service: string
+    /**
+     * Log level
+     */
+    level: "debug" | "info" | "error" | "warn"
+    /**
+     * Log message
+     */
+    message: string
+    /**
+     * Additional metadata for the log entry
+     */
+    extra?: {
+      [key: string]: unknown
+    }
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/log"
+}
+
+export type AppLogErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type AppLogError = AppLogErrors[keyof AppLogErrors]
+
+export type AppLogResponses = {
+  /**
+   * Log entry written successfully
+   */
+  200: boolean
+}
+
+export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
+
+export type AppAgentsData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/agent"
+}
+
+export type AppAgentsResponses = {
+  /**
+   * List of agents
+   */
+  200: Array<Agent>
+}
+
+export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses]
+
+export type McpStatusData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/mcp"
+}
+
+export type McpStatusResponses = {
+  /**
+   * MCP server status
+   */
+  200: {
+    [key: string]: McpStatus
+  }
+}
+
+export type McpStatusResponse = McpStatusResponses[keyof McpStatusResponses]
+
+export type McpAddData = {
+  body?: {
+    name: string
+    config: McpLocalConfig | McpRemoteConfig
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/mcp"
+}
+
+export type McpAddErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type McpAddError = McpAddErrors[keyof McpAddErrors]
+
+export type McpAddResponses = {
+  /**
+   * MCP server added successfully
+   */
+  200: {
+    [key: string]: McpStatus
+  }
+}
+
+export type McpAddResponse = McpAddResponses[keyof McpAddResponses]
+
+export type McpAuthRemoveData = {
+  body?: never
+  path: {
+    name: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/mcp/{name}/auth"
+}
+
+export type McpAuthRemoveErrors = {
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type McpAuthRemoveError = McpAuthRemoveErrors[keyof McpAuthRemoveErrors]
+
+export type McpAuthRemoveResponses = {
+  /**
+   * OAuth credentials removed
+   */
+  200: {
+    success: true
+  }
+}
+
+export type McpAuthRemoveResponse = McpAuthRemoveResponses[keyof McpAuthRemoveResponses]
+
+export type McpAuthStartData = {
+  body?: never
+  path: {
+    name: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/mcp/{name}/auth"
+}
+
+export type McpAuthStartErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type McpAuthStartError = McpAuthStartErrors[keyof McpAuthStartErrors]
+
+export type McpAuthStartResponses = {
+  /**
+   * OAuth flow started
+   */
+  200: {
+    /**
+     * URL to open in browser for authorization
+     */
+    authorizationUrl: string
+  }
+}
+
+export type McpAuthStartResponse = McpAuthStartResponses[keyof McpAuthStartResponses]
+
+export type McpAuthCallbackData = {
+  body?: {
+    /**
+     * Authorization code from OAuth callback
+     */
+    code: string
+  }
+  path: {
+    name: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/mcp/{name}/auth/callback"
+}
+
+export type McpAuthCallbackErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type McpAuthCallbackError = McpAuthCallbackErrors[keyof McpAuthCallbackErrors]
+
+export type McpAuthCallbackResponses = {
+  /**
+   * OAuth authentication completed
+   */
+  200: McpStatus
+}
+
+export type McpAuthCallbackResponse = McpAuthCallbackResponses[keyof McpAuthCallbackResponses]
+
+export type McpAuthAuthenticateData = {
+  body?: never
+  path: {
+    name: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/mcp/{name}/auth/authenticate"
+}
+
+export type McpAuthAuthenticateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+  /**
+   * Not found
+   */
+  404: NotFoundError
+}
+
+export type McpAuthAuthenticateError = McpAuthAuthenticateErrors[keyof McpAuthAuthenticateErrors]
+
+export type McpAuthAuthenticateResponses = {
+  /**
+   * OAuth authentication completed
+   */
+  200: McpStatus
+}
+
+export type McpAuthAuthenticateResponse = McpAuthAuthenticateResponses[keyof McpAuthAuthenticateResponses]
+
+export type LspStatusData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/lsp"
+}
+
+export type LspStatusResponses = {
+  /**
+   * LSP server status
+   */
+  200: Array<LspStatus>
+}
+
+export type LspStatusResponse = LspStatusResponses[keyof LspStatusResponses]
+
+export type FormatterStatusData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/formatter"
+}
+
+export type FormatterStatusResponses = {
+  /**
+   * Formatter status
+   */
+  200: Array<FormatterStatus>
+}
+
+export type FormatterStatusResponse = FormatterStatusResponses[keyof FormatterStatusResponses]
+
+export type TuiAppendPromptData = {
+  body?: {
+    text: string
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/append-prompt"
+}
+
+export type TuiAppendPromptErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type TuiAppendPromptError = TuiAppendPromptErrors[keyof TuiAppendPromptErrors]
+
+export type TuiAppendPromptResponses = {
+  /**
+   * Prompt processed successfully
+   */
+  200: boolean
+}
+
+export type TuiAppendPromptResponse = TuiAppendPromptResponses[keyof TuiAppendPromptResponses]
+
+export type TuiOpenHelpData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/open-help"
+}
+
+export type TuiOpenHelpResponses = {
+  /**
+   * Help dialog opened successfully
+   */
+  200: boolean
+}
+
+export type TuiOpenHelpResponse = TuiOpenHelpResponses[keyof TuiOpenHelpResponses]
+
+export type TuiOpenSessionsData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/open-sessions"
+}
+
+export type TuiOpenSessionsResponses = {
+  /**
+   * Session dialog opened successfully
+   */
+  200: boolean
+}
+
+export type TuiOpenSessionsResponse = TuiOpenSessionsResponses[keyof TuiOpenSessionsResponses]
+
+export type TuiOpenThemesData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/open-themes"
+}
+
+export type TuiOpenThemesResponses = {
+  /**
+   * Theme dialog opened successfully
+   */
+  200: boolean
+}
+
+export type TuiOpenThemesResponse = TuiOpenThemesResponses[keyof TuiOpenThemesResponses]
+
+export type TuiOpenModelsData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/open-models"
+}
+
+export type TuiOpenModelsResponses = {
+  /**
+   * Model dialog opened successfully
+   */
+  200: boolean
+}
+
+export type TuiOpenModelsResponse = TuiOpenModelsResponses[keyof TuiOpenModelsResponses]
+
+export type TuiSubmitPromptData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/submit-prompt"
+}
+
+export type TuiSubmitPromptResponses = {
+  /**
+   * Prompt submitted successfully
+   */
+  200: boolean
+}
+
+export type TuiSubmitPromptResponse = TuiSubmitPromptResponses[keyof TuiSubmitPromptResponses]
+
+export type TuiClearPromptData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/clear-prompt"
+}
+
+export type TuiClearPromptResponses = {
+  /**
+   * Prompt cleared successfully
+   */
+  200: boolean
+}
+
+export type TuiClearPromptResponse = TuiClearPromptResponses[keyof TuiClearPromptResponses]
+
+export type TuiExecuteCommandData = {
+  body?: {
+    command: string
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/execute-command"
+}
+
+export type TuiExecuteCommandErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type TuiExecuteCommandError = TuiExecuteCommandErrors[keyof TuiExecuteCommandErrors]
+
+export type TuiExecuteCommandResponses = {
+  /**
+   * Command executed successfully
+   */
+  200: boolean
+}
+
+export type TuiExecuteCommandResponse = TuiExecuteCommandResponses[keyof TuiExecuteCommandResponses]
+
+export type TuiShowToastData = {
+  body?: {
+    title?: string
+    message: string
+    variant: "info" | "success" | "warning" | "error"
+    /**
+     * Duration in milliseconds
+     */
+    duration?: number
+  }
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/show-toast"
+}
+
+export type TuiShowToastResponses = {
+  /**
+   * Toast notification shown successfully
+   */
+  200: boolean
+}
+
+export type TuiShowToastResponse = TuiShowToastResponses[keyof TuiShowToastResponses]
+
+export type TuiPublishData = {
+  body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/publish"
+}
+
+export type TuiPublishErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type TuiPublishError = TuiPublishErrors[keyof TuiPublishErrors]
+
+export type TuiPublishResponses = {
+  /**
+   * Event published successfully
+   */
+  200: boolean
+}
+
+export type TuiPublishResponse = TuiPublishResponses[keyof TuiPublishResponses]
+
+export type TuiControlNextData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/control/next"
+}
+
+export type TuiControlNextResponses = {
+  /**
+   * Next TUI request
+   */
+  200: {
+    path: string
+    body: unknown
+  }
+}
+
+export type TuiControlNextResponse = TuiControlNextResponses[keyof TuiControlNextResponses]
+
+export type TuiControlResponseData = {
+  body?: unknown
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/tui/control/response"
+}
+
+export type TuiControlResponseResponses = {
+  /**
+   * Response submitted successfully
+   */
+  200: boolean
+}
+
+export type TuiControlResponseResponse = TuiControlResponseResponses[keyof TuiControlResponseResponses]
+
+export type AuthSetData = {
+  body?: Auth
+  path: {
+    providerID: string
+  }
+  query?: {
+    directory?: string
+  }
+  url: "/auth/{providerID}"
+}
+
+export type AuthSetErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type AuthSetError = AuthSetErrors[keyof AuthSetErrors]
+
+export type AuthSetResponses = {
+  /**
+   * Successfully set authentication credentials
+   */
+  200: boolean
+}
+
+export type AuthSetResponse = AuthSetResponses[keyof AuthSetResponses]
+
+export type EventSubscribeData = {
+  body?: never
+  path?: never
+  query?: {
+    directory?: string
+  }
+  url: "/event"
+}
+
+export type EventSubscribeResponses = {
+  /**
+   * Event stream
+   */
+  200: Event
+}
+
+export type EventSubscribeResponse = EventSubscribeResponses[keyof EventSubscribeResponses]

+ 21 - 0
packages/sdk/js/src/v2/index.ts

@@ -0,0 +1,21 @@
+export * from "./client.js"
+export * from "./server.js"
+
+import { createOpencodeClient } from "./client.js"
+import { createOpencodeServer } from "./server.js"
+import type { ServerOptions } from "./server.js"
+
+export async function createOpencode(options?: ServerOptions) {
+  const server = await createOpencodeServer({
+    ...options,
+  })
+
+  const client = createOpencodeClient({
+    baseUrl: server.url,
+  })
+
+  return {
+    client,
+    server,
+  }
+}

+ 120 - 0
packages/sdk/js/src/v2/server.ts

@@ -0,0 +1,120 @@
+import { spawn } from "node:child_process"
+import { type Config } from "./gen/types.gen.js"
+
+export type ServerOptions = {
+  hostname?: string
+  port?: number
+  signal?: AbortSignal
+  timeout?: number
+  config?: Config
+}
+
+export type TuiOptions = {
+  project?: string
+  model?: string
+  session?: string
+  agent?: string
+  signal?: AbortSignal
+  config?: Config
+}
+
+export async function createOpencodeServer(options?: ServerOptions) {
+  options = Object.assign(
+    {
+      hostname: "127.0.0.1",
+      port: 4096,
+      timeout: 5000,
+    },
+    options ?? {},
+  )
+
+  const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], {
+    signal: options.signal,
+    env: {
+      ...process.env,
+      OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}),
+    },
+  })
+
+  const url = await new Promise<string>((resolve, reject) => {
+    const id = setTimeout(() => {
+      reject(new Error(`Timeout waiting for server to start after ${options.timeout}ms`))
+    }, options.timeout)
+    let output = ""
+    proc.stdout?.on("data", (chunk) => {
+      output += chunk.toString()
+      const lines = output.split("\n")
+      for (const line of lines) {
+        if (line.startsWith("opencode server listening")) {
+          const match = line.match(/on\s+(https?:\/\/[^\s]+)/)
+          if (!match) {
+            throw new Error(`Failed to parse server url from output: ${line}`)
+          }
+          clearTimeout(id)
+          resolve(match[1]!)
+          return
+        }
+      }
+    })
+    proc.stderr?.on("data", (chunk) => {
+      output += chunk.toString()
+    })
+    proc.on("exit", (code) => {
+      clearTimeout(id)
+      let msg = `Server exited with code ${code}`
+      if (output.trim()) {
+        msg += `\nServer output: ${output}`
+      }
+      reject(new Error(msg))
+    })
+    proc.on("error", (error) => {
+      clearTimeout(id)
+      reject(error)
+    })
+    if (options.signal) {
+      options.signal.addEventListener("abort", () => {
+        clearTimeout(id)
+        reject(new Error("Aborted"))
+      })
+    }
+  })
+
+  return {
+    url,
+    close() {
+      proc.kill()
+    },
+  }
+}
+
+export function createOpencodeTui(options?: TuiOptions) {
+  const args = []
+
+  if (options?.project) {
+    args.push(`--project=${options.project}`)
+  }
+  if (options?.model) {
+    args.push(`--model=${options.model}`)
+  }
+  if (options?.session) {
+    args.push(`--session=${options.session}`)
+  }
+  if (options?.agent) {
+    args.push(`--agent=${options.agent}`)
+  }
+
+  const proc = spawn(`opencode`, args, {
+    signal: options?.signal,
+    stdio: "inherit",
+    env: {
+      ...process.env,
+      OPENCODE_CONFIG_CONTENT: JSON.stringify(options?.config ?? {}),
+    },
+  })
+
+  return {
+    close() {
+      proc.kill()
+    },
+  }
+}

File diff ditekan karena terlalu besar
+ 252 - 100
packages/sdk/openapi.json


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini