@vantran-se/openchamber-web 1.24.4 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +2 -8
  2. package/bin/lib/commands-connect-url.js +3 -8
  3. package/bin/lib/commands-tunnel.js +3 -0
  4. package/dist/assets/{CodeMirrorCommentWidgets-B4a0JkUz.js → CodeMirrorCommentWidgets-B82Oye3V.js} +1 -1
  5. package/dist/assets/DiffView-DJLmWn6Z.js +4 -0
  6. package/dist/assets/FilesView-DjksmZRo.js +17 -0
  7. package/dist/assets/{GitView-Byu2DY7s.js → GitView-DiUsUIgr.js} +4 -4
  8. package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-CYwWYLr3.js} +1 -1
  9. package/dist/assets/GuestSurfacePane-CmctyRI4.js +1 -0
  10. package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-DOHHcbgJ.js} +1 -1
  11. package/dist/assets/LinearIssuesView-DaqnylcV.js +1 -0
  12. package/dist/assets/MarkdownImageGallery-HJNvWt7L.js +1 -0
  13. package/dist/assets/MarkdownRendererImpl-nDZRGg2I.js +12 -0
  14. package/dist/assets/OnboardingScreen-C2B5Bsof.js +1 -0
  15. package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-C4FLBjpt.js} +1 -1
  16. package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-C_1S9c9n.js} +1 -1
  17. package/dist/assets/PlanView-_eZvb-G5.js +5 -0
  18. package/dist/assets/PluginPane-sM8fcZIV.js +3 -0
  19. package/dist/assets/SettingsView-BRfr46hb.js +29 -0
  20. package/dist/assets/SettingsWindow-CcUDwQHg.js +1 -0
  21. package/dist/assets/SubmoduleDiffSummary-CrN1ZGwl.js +1 -0
  22. package/dist/assets/ToolOutputDialog-Cij4jI09.js +15 -0
  23. package/dist/assets/ToolPartDiffPreview-BXzwQ-JY.js +8 -0
  24. package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-DSWTLJNH.js} +4 -4
  25. package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-CpUUgY8H.js} +2 -2
  26. package/dist/assets/appThemeRegistry-B5tHTkR8.js +1 -0
  27. package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-D7D8qV8p.js} +1 -1
  28. package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-cq9uQ30i.js} +1 -1
  29. package/dist/assets/de-CUO1-KqF.js +10 -0
  30. package/dist/assets/es-BlbC6sGJ.js +16 -0
  31. package/dist/assets/fr-D2nwe_0W.js +14 -0
  32. package/dist/assets/index-BQOoYIOu.js +1 -0
  33. package/dist/assets/index-CLcM8HKx.css +1 -0
  34. package/dist/assets/index-CvLgnuNI.js +41 -0
  35. package/dist/assets/{isDiffPlainText-CO4e5J_S.js → isDiffPlainText-D0obJ2p8.js} +1 -1
  36. package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-DB3_rjyW.js} +1 -1
  37. package/dist/assets/ja-CA7vnDsZ.js +16 -0
  38. package/dist/assets/ko-Chy6a9l-.js +16 -0
  39. package/dist/assets/main-B9JFK3vF.js +896 -0
  40. package/dist/assets/main-Bhiqnrhd.js +2 -0
  41. package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-tfqOFaBd.js} +4 -4
  42. package/dist/assets/miniChat-B0UOL5zA.js +2 -0
  43. package/dist/assets/mobile-BJppFIEo.js +2 -0
  44. package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-Db6KuwCP.js} +3 -3
  45. package/dist/assets/pl-CTR5A_1P.js +16 -0
  46. package/dist/assets/pt-BR-BE1O1KEx.js +16 -0
  47. package/dist/assets/renderElectronMiniChatApp-Yz5sfoFh.js +2 -0
  48. package/dist/assets/renderMobileApp-QQ-yB8aY.js +25 -0
  49. package/dist/assets/runtimeEndpointReset-DKDdzRzm.js +27 -0
  50. package/dist/assets/scheduledTasksApi-C5sCuNqf.js +1 -0
  51. package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-xURoo5t_.js} +1 -1
  52. package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-BZLGuYTU.js} +1 -1
  53. package/dist/assets/tr-Bh5vK6cW.js +7 -0
  54. package/dist/assets/uk-CLDkZFy_.js +16 -0
  55. package/dist/assets/useAppFontEffects-BxrtCnfR.js +7337 -0
  56. package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CeXuR-SR.js} +2 -2
  57. package/dist/assets/useRootScrollLock-DgWxezc1.js +2 -0
  58. package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-DOuaQUZ0.js} +2 -2
  59. package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
  60. package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
  61. package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
  62. package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
  63. package/dist/assets/{web-components-DxX8bZ1o.js → web-components-63H1h21j.js} +2 -2
  64. package/dist/assets/zh-CN-DODgcmYj.js +16 -0
  65. package/dist/assets/zh-TW-ByNfIwLA.js +16 -0
  66. package/dist/index.html +13 -7
  67. package/dist/mini-chat.html +5 -5
  68. package/dist/mobile.html +5 -5
  69. package/package.json +6 -5
  70. package/server/index.d.ts +1 -0
  71. package/server/index.js +246 -91
  72. package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
  73. package/server/lib/agent-tool/runtime.js +135 -54
  74. package/server/lib/agent-tool/runtime.test.js +155 -87
  75. package/server/lib/browser-control/DOCUMENTATION.md +37 -2
  76. package/server/lib/browser-control/provider.js +217 -0
  77. package/server/lib/browser-control/provider.test.js +251 -0
  78. package/server/lib/context-obligatory/runtime.js +61 -50
  79. package/server/lib/context-obligatory/runtime.test.js +62 -162
  80. package/server/lib/event-stream/DOCUMENTATION.md +12 -1
  81. package/server/lib/event-stream/delta-coalescer.js +0 -0
  82. package/server/lib/event-stream/delta-coalescer.test.js +68 -57
  83. package/server/lib/event-stream/directory-ws-bridge.js +1 -1
  84. package/server/lib/event-stream/global-hub.js +10 -1
  85. package/server/lib/event-stream/global-hub.test.js +27 -26
  86. package/server/lib/event-stream/index.js +6 -0
  87. package/server/lib/event-stream/protocol.js +26 -19
  88. package/server/lib/event-stream/protocol.test.js +18 -7
  89. package/server/lib/event-stream/rebind.test.js +5 -5
  90. package/server/lib/event-stream/runtime.test.js +17 -15
  91. package/server/lib/event-stream/translate-v2.js +313 -0
  92. package/server/lib/event-stream/translate-v2.test.js +209 -0
  93. package/server/lib/event-stream/upstream-reader.test.js +16 -16
  94. package/server/lib/fs/DOCUMENTATION.md +4 -2
  95. package/server/lib/fs/byte-range.js +35 -0
  96. package/server/lib/fs/byte-range.test.js +31 -0
  97. package/server/lib/fs/routes.js +47 -42
  98. package/server/lib/fs/routes.test.js +123 -18
  99. package/server/lib/git/service.js +12 -0
  100. package/server/lib/github/DOCUMENTATION.md +1 -1
  101. package/server/lib/github/pr-status.js +22 -2
  102. package/server/lib/github/pr-status.test.js +27 -14
  103. package/server/lib/guests/DOCUMENTATION.md +13 -0
  104. package/server/lib/guests/catalog.js +12 -0
  105. package/server/lib/guests/catalog.test.js +33 -0
  106. package/server/lib/guests/routes.js +10 -1
  107. package/server/lib/guests/service.js +192 -14
  108. package/server/lib/guests/service.test.js +201 -1
  109. package/server/lib/guests/surface.js +630 -0
  110. package/server/lib/guests/surface.test.js +486 -0
  111. package/server/lib/markdown-image-grants/routes.js +18 -5
  112. package/server/lib/markdown-image-grants/routes.test.js +9 -6
  113. package/server/lib/message-queue/DOCUMENTATION.md +33 -22
  114. package/server/lib/message-queue/runtime.js +155 -100
  115. package/server/lib/message-queue/runtime.test.js +168 -82
  116. package/server/lib/notifications/DOCUMENTATION.md +28 -0
  117. package/server/lib/notifications/control-stream.http.test.js +68 -0
  118. package/server/lib/notifications/emitter-runtime.js +15 -0
  119. package/server/lib/notifications/emitter-runtime.test.js +32 -0
  120. package/server/lib/notifications/push-runtime.js +5 -1
  121. package/server/lib/notifications/push-runtime.test.js +2 -2
  122. package/server/lib/notifications/routes.js +1 -1
  123. package/server/lib/notifications/runtime.js +115 -26
  124. package/server/lib/notifications/template-runtime.js +13 -15
  125. package/server/lib/notifications/template-runtime.test.js +16 -9
  126. package/server/lib/notifications/trigger-runtime.test.js +187 -0
  127. package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
  128. package/server/lib/openchamber-control/actions.js +3 -2
  129. package/server/lib/openchamber-control/error.js +1 -1
  130. package/server/lib/openchamber-control/file-open.js +59 -0
  131. package/server/lib/openchamber-control/file-open.test.js +57 -0
  132. package/server/lib/openchamber-control/routes.test.js +12 -0
  133. package/server/lib/openchamber-control/service.js +102 -49
  134. package/server/lib/openchamber-control/service.test.js +84 -44
  135. package/server/lib/openchamber-sessions/archive-store.js +224 -0
  136. package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
  137. package/server/lib/openchamber-sessions/opencode-client.js +22 -0
  138. package/server/lib/openchamber-sessions/routes.js +398 -328
  139. package/server/lib/openchamber-sessions/routes.test.js +777 -740
  140. package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
  141. package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
  142. package/server/lib/opencode/DOCUMENTATION.md +449 -29
  143. package/server/lib/opencode/agents.js +233 -327
  144. package/server/lib/opencode/auth.js +41 -44
  145. package/server/lib/opencode/auth.test.js +92 -0
  146. package/server/lib/opencode/cli-upgrade.d.ts +4 -0
  147. package/server/lib/opencode/cli-upgrade.js +18 -0
  148. package/server/lib/opencode/cli-upgrade.test.js +36 -0
  149. package/server/lib/opencode/commands.js +164 -145
  150. package/server/lib/opencode/compatibility.d.ts +16 -0
  151. package/server/lib/opencode/compatibility.js +88 -0
  152. package/server/lib/opencode/compatibility.test.js +62 -0
  153. package/server/lib/opencode/config-entity-routes.js +58 -29
  154. package/server/lib/opencode/config-entity-v2.test.js +290 -0
  155. package/server/lib/opencode/config-mutation-response.js +17 -7
  156. package/server/lib/opencode/config-mutation-response.test.js +4 -7
  157. package/server/lib/opencode/config-paths.test.js +2 -2
  158. package/server/lib/opencode/config-v2.d.ts +183 -0
  159. package/server/lib/opencode/config-v2.js +801 -0
  160. package/server/lib/opencode/config-v2.test.js +466 -0
  161. package/server/lib/opencode/credential-db.js +144 -0
  162. package/server/lib/opencode/credential-db.test.js +104 -0
  163. package/server/lib/opencode/env-runtime.js +48 -9
  164. package/server/lib/opencode/env-runtime.test.js +45 -0
  165. package/server/lib/opencode/feature-routes-runtime.js +15 -5
  166. package/server/lib/opencode/lifecycle-process.test.js +7 -4
  167. package/server/lib/opencode/lifecycle.js +85 -30
  168. package/server/lib/opencode/lifecycle.test.js +80 -8
  169. package/server/lib/opencode/login-shell-env.js +27 -0
  170. package/server/lib/opencode/managed-config-file.js +119 -0
  171. package/server/lib/opencode/managed-config-file.test.js +152 -0
  172. package/server/lib/opencode/managed-plugin-config.js +33 -13
  173. package/server/lib/opencode/mcp.js +43 -149
  174. package/server/lib/opencode/network-runtime.js +6 -7
  175. package/server/lib/opencode/openchamber-routes.test.js +2 -5
  176. package/server/lib/opencode/plugin-routes.js +3 -3
  177. package/server/lib/opencode/plugin-routes.test.js +6 -24
  178. package/server/lib/opencode/plugins.js +91 -40
  179. package/server/lib/opencode/plugins.test.js +28 -15
  180. package/server/lib/opencode/providers.js +63 -83
  181. package/server/lib/opencode/providers.test.js +128 -59
  182. package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
  183. package/server/lib/opencode/proxy.js +179 -129
  184. package/server/lib/opencode/pwa-manifest-routes.js +4 -2
  185. package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
  186. package/server/lib/opencode/response-envelope.js +14 -0
  187. package/server/lib/opencode/response-envelope.test.js +23 -0
  188. package/server/lib/opencode/routes-upgrade.test.js +73 -116
  189. package/server/lib/opencode/routes.js +121 -428
  190. package/server/lib/opencode/server-utils-runtime.js +10 -4
  191. package/server/lib/opencode/session-activity.js +78 -0
  192. package/server/lib/opencode/session-runtime.js +21 -10
  193. package/server/lib/opencode/session-runtime.test.js +9 -9
  194. package/server/lib/opencode/settings-helpers.js +9 -9
  195. package/server/lib/opencode/settings-helpers.test.js +7 -12
  196. package/server/lib/opencode/settings-registry.json +6 -8
  197. package/server/lib/opencode/settings-runtime.js +18 -1
  198. package/server/lib/opencode/settings-runtime.test.js +31 -2
  199. package/server/lib/opencode/shared.js +54 -22
  200. package/server/lib/opencode/shared.test.js +8 -4
  201. package/server/lib/opencode/shutdown-runtime.js +54 -7
  202. package/server/lib/opencode/shutdown-runtime.test.js +224 -30
  203. package/server/lib/opencode/skill-routes.js +23 -24
  204. package/server/lib/opencode/skills.js +7 -5
  205. package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
  206. package/server/lib/opencode/upgrade-capability.test.js +1 -1
  207. package/server/lib/opencode/v1-migration-topup.js +288 -0
  208. package/server/lib/opencode/v1-migration-topup.test.js +266 -0
  209. package/server/lib/opencode/v2-install.d.ts +8 -0
  210. package/server/lib/opencode/v2-install.js +158 -0
  211. package/server/lib/opencode/v2-install.test.js +124 -0
  212. package/server/lib/opencode/watcher.js +10 -3
  213. package/server/lib/opencode/watcher.test.js +17 -21
  214. package/server/lib/opencode/websearch-config.js +66 -0
  215. package/server/lib/package-manager.js +55 -68
  216. package/server/lib/package-manager.test.js +241 -75
  217. package/server/lib/permission-auto-accept/runtime.js +38 -22
  218. package/server/lib/permission-auto-accept/runtime.test.js +39 -17
  219. package/server/lib/quota/DOCUMENTATION.md +4 -0
  220. package/server/lib/quota/providers/xai.js +4 -4
  221. package/server/lib/relay/tunnel-host.js +4 -1
  222. package/server/lib/relay/tunnel-host.test.js +7 -0
  223. package/server/lib/routing/DOCUMENTATION.md +52 -21
  224. package/server/lib/routing/defaults.js +23 -6
  225. package/server/lib/routing/jev.js +17 -7
  226. package/server/lib/routing/routes.http.test.js +68 -33
  227. package/server/lib/routing/routes.js +65 -22
  228. package/server/lib/routing/runtime.js +145 -48
  229. package/server/lib/routing/runtime.test.js +79 -55
  230. package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
  231. package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
  232. package/server/lib/scheduled-tasks/runtime.js +81 -74
  233. package/server/lib/scheduled-tasks/runtime.test.js +53 -1
  234. package/server/lib/session-assist/DOCUMENTATION.md +8 -3
  235. package/server/lib/session-assist/context.js +122 -46
  236. package/server/lib/session-assist/context.test.js +117 -49
  237. package/server/lib/session-assist/runtime.js +52 -22
  238. package/server/lib/session-assist/runtime.test.js +145 -173
  239. package/server/lib/session-goal/DOCUMENTATION.md +23 -15
  240. package/server/lib/session-goal/create.js +9 -12
  241. package/server/lib/session-goal/create.test.js +27 -28
  242. package/server/lib/session-goal/runtime.js +164 -77
  243. package/server/lib/session-goal/runtime.test.js +271 -519
  244. package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
  245. package/server/lib/session-knowledge/runtime.js +59 -41
  246. package/server/lib/session-knowledge/runtime.test.js +90 -20
  247. package/server/lib/small-model/DOCUMENTATION.md +154 -208
  248. package/server/lib/small-model/client.js +113 -0
  249. package/server/lib/small-model/index.js +296 -158
  250. package/server/lib/small-model/index.test.js +423 -256
  251. package/server/lib/small-model/routes.js +1 -1
  252. package/server/lib/spaces/DOCUMENTATION.md +807 -0
  253. package/server/lib/spaces/code-in-bait.js +185 -0
  254. package/server/lib/spaces/code-in.js +641 -0
  255. package/server/lib/spaces/code-in.test.js +1048 -0
  256. package/server/lib/spaces/errors.js +8 -0
  257. package/server/lib/spaces/exec-http.js +94 -0
  258. package/server/lib/spaces/gatekeeper-channel.js +202 -0
  259. package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
  260. package/server/lib/spaces/gatekeeper-program.cjs +914 -0
  261. package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
  262. package/server/lib/spaces/hardening.js +457 -0
  263. package/server/lib/spaces/hardening.test.js +368 -0
  264. package/server/lib/spaces/host-git.js +127 -0
  265. package/server/lib/spaces/host-git.test.js +137 -0
  266. package/server/lib/spaces/labels.js +234 -0
  267. package/server/lib/spaces/labels.test.js +219 -0
  268. package/server/lib/spaces/layout.js +184 -0
  269. package/server/lib/spaces/layout.test.js +30 -0
  270. package/server/lib/spaces/manager.js +82 -0
  271. package/server/lib/spaces/manager.test.js +114 -0
  272. package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
  273. package/server/lib/spaces/places/contract-suite.js +156 -0
  274. package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
  275. package/server/lib/spaces/places/contract.test.js +6 -0
  276. package/server/lib/spaces/places/docker-engine.js +126 -0
  277. package/server/lib/spaces/places/docker-live-support.js +202 -0
  278. package/server/lib/spaces/places/docker-tools.js +186 -0
  279. package/server/lib/spaces/places/docker.js +587 -0
  280. package/server/lib/spaces/places/docker.test.js +1610 -0
  281. package/server/lib/spaces/places/escape-suite.js +1018 -0
  282. package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
  283. package/server/lib/spaces/places/fake-docker.js +375 -0
  284. package/server/lib/spaces/places/memory-place.js +103 -0
  285. package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
  286. package/server/lib/spaces/places/registry.js +43 -0
  287. package/server/lib/spaces/places/registry.test.js +50 -0
  288. package/server/lib/spaces/places/server.docker.live.test.js +225 -0
  289. package/server/lib/spaces/run-command.js +261 -0
  290. package/server/lib/spaces/run-command.test.js +396 -0
  291. package/server/lib/spaces/space-server.js +190 -0
  292. package/server/lib/spaces/space-server.test.js +335 -0
  293. package/server/lib/spaces/tools-filler.js +82 -0
  294. package/server/lib/spaces/tools-filler.test.js +148 -0
  295. package/server/lib/spaces/tools-pack.js +49 -0
  296. package/server/lib/spaces/tools-pack.test.js +57 -0
  297. package/server/lib/spaces/tools.js +138 -0
  298. package/server/lib/spaces/tools.test.js +153 -0
  299. package/server/lib/tts/openai-sdk.js +11 -0
  300. package/server/lib/tts/service.js +7 -5
  301. package/server/lib/tts/stt.js +3 -2
  302. package/server/lib/tunnels/DOCUMENTATION.md +2 -0
  303. package/server/lib/tunnels/routes.js +7 -0
  304. package/server/lib/tunnels/routes.test.js +55 -0
  305. package/server/lib/ui-auth/ui-auth.js +38 -2
  306. package/server/lib/ui-auth/ui-auth.test.js +42 -0
  307. package/server/lib/ui-auth/ui-passkeys.js +12 -10
  308. package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
  309. package/server/lib/walkthrough/pull-request.js +61 -8
  310. package/server/lib/walkthrough/pull-request.test.js +54 -1
  311. package/server/lib/walkthrough/routes.js +20 -0
  312. package/server/lib/walkthrough/routes.test.js +19 -0
  313. package/server/opencode-proxy.test.js +190 -166
  314. package/server/opencode-worktree-readiness.test.js +5 -4
  315. package/dist/assets/DiffView-B2CD9wC8.js +0 -4
  316. package/dist/assets/FilesView-C4vMFclc.js +0 -13
  317. package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
  318. package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
  319. package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
  320. package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
  321. package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
  322. package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
  323. package/dist/assets/SettingsView-CSinuH-1.js +0 -29
  324. package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
  325. package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
  326. package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
  327. package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
  328. package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
  329. package/dist/assets/de-Bro6_zyl.js +0 -10
  330. package/dist/assets/es-Bb9XQZvd.js +0 -16
  331. package/dist/assets/fr-Dgfzn-IL.js +0 -14
  332. package/dist/assets/index-CtQNmqUL.js +0 -1
  333. package/dist/assets/index-DqaMWNrC.css +0 -1
  334. package/dist/assets/index-Dqc6USl3.js +0 -40
  335. package/dist/assets/ja-BYsakGFh.js +0 -16
  336. package/dist/assets/ko-BSaQz9aN.js +0 -16
  337. package/dist/assets/main-B62_pjL5.js +0 -2
  338. package/dist/assets/main-BEdcOI2P.js +0 -896
  339. package/dist/assets/miniChat-LT8Rqofr.js +0 -2
  340. package/dist/assets/mobile-CqarEynh.js +0 -2
  341. package/dist/assets/pl-OIviPdoj.js +0 -16
  342. package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
  343. package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
  344. package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
  345. package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
  346. package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
  347. package/dist/assets/tr-CQI-KPre.js +0 -7
  348. package/dist/assets/uk-CqW7R4DA.js +0 -16
  349. package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
  350. package/dist/assets/useRootScrollLock-Cpf1qoGp.js +0 -2
  351. package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
  352. package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
  353. package/dist/assets/zh-CN-CVZG_o_J.js +0 -16
  354. package/dist/assets/zh-TW-D5ZxzCZp.js +0 -16
  355. package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
  356. package/server/lib/routing/feature-flag.js +0 -11
  357. package/server/lib/small-model/call.js +0 -851
  358. package/server/lib/small-model/call.test.js +0 -1292
  359. package/server/lib/small-model/catalog.js +0 -13
  360. package/server/lib/small-model/resolve.js +0 -131
  361. package/server/lib/small-model/resolve.test.js +0 -197
  362. package/server/lib/small-model/runtime-providers.js +0 -164
  363. package/server/lib/small-model/runtime-providers.test.js +0 -118
  364. package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
  365. package/server/lib/system-prompt/runtime.js +0 -49
  366. package/server/lib/system-prompt/runtime.test.js +0 -76
  367. package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
@@ -79,12 +79,14 @@ describe('OpenCode API proxy agent wiring', () => {
79
79
  }
80
80
  });
81
81
 
82
- it('shares one agent instance across the API and OAuth proxies', () => {
82
+ it('resolves an agent for the API proxy', () => {
83
+ // v1 also built a long-deadline proxy for the interactive provider/MCP
84
+ // OAuth callbacks; v2 has neither route, so there is one proxy left.
83
85
  registerOpenCodeProxy(createStubApp(), createStubDeps(managedState()));
84
86
 
85
87
  const agents = agentsFromCalls();
86
88
 
87
- expect(agents.length).toBeGreaterThan(1);
89
+ expect(agents.length).toBeGreaterThan(0);
88
90
  expect(agents.every(Boolean)).toBe(true);
89
91
  expect(new Set(agents).size).toBe(1);
90
92
  });
@@ -210,24 +210,30 @@ export const createSseBoundaryTracker = () => {
210
210
  };
211
211
  };
212
212
 
213
+ /**
214
+ * Fields a session list is allowed to carry to the browser.
215
+ *
216
+ * The list is an allowlist, not a blocklist: OpenCode keeps adding to
217
+ * `SessionInfo`, and a session list is fetched constantly, so anything heavy
218
+ * that appears later must not silently start crossing the wire. `revert.files`
219
+ * and `revert.snapshot` are the expensive parts and are dropped below;
220
+ * `permissions` is a per-session ruleset the list view never reads.
221
+ */
213
222
  const SESSION_LIST_ALLOWED_FIELDS = [
214
223
  'id',
215
- 'slug',
216
- 'projectID',
217
- 'workspaceID',
218
- 'directory',
219
- 'path',
220
224
  'parentID',
225
+ 'projectID',
226
+ 'location',
227
+ 'subpath',
221
228
  'title',
222
229
  'agent',
223
230
  'model',
224
- 'version',
225
- 'time',
226
231
  'cost',
227
232
  'tokens',
228
- 'share',
233
+ 'outcome',
234
+ 'time',
229
235
  'metadata',
230
- 'project',
236
+ 'fork',
231
237
  ];
232
238
 
233
239
  const sanitizeSessionListItem = (session) => {
@@ -242,13 +248,8 @@ const sanitizeSessionListItem = (session) => {
242
248
  }
243
249
  }
244
250
 
245
- const summary = session.summary;
246
- if (summary && typeof summary === 'object' && !Array.isArray(summary)) {
247
- const summaryWithoutDiffs = { ...summary };
248
- delete summaryWithoutDiffs.diffs;
249
- sanitized.summary = summaryWithoutDiffs;
250
- }
251
-
251
+ // Only the revert marker: the staged file list and its snapshot are what make
252
+ // a reverted session's record large.
252
253
  const revert = session.revert;
253
254
  if (revert && typeof revert === 'object' && !Array.isArray(revert)) {
254
255
  const revertMarker = {};
@@ -266,18 +267,28 @@ const sanitizeSessionListItem = (session) => {
266
267
  return sanitized;
267
268
  };
268
269
 
270
+ /**
271
+ * Preserve the V2 pagination envelope while sanitizing session records.
272
+ */
269
273
  const sanitizeSessionListPayload = (payload) => {
270
- if (!Array.isArray(payload)) {
271
- return payload;
274
+ if (Array.isArray(payload)) {
275
+ return payload.map((session) => sanitizeSessionListItem(session));
272
276
  }
273
- return payload.map((session) => sanitizeSessionListItem(session));
277
+ if (payload && typeof payload === 'object' && Array.isArray(payload.data)) {
278
+ return { ...payload, data: payload.data.map((session) => sanitizeSessionListItem(session)) };
279
+ }
280
+ return payload;
281
+ };
282
+
283
+ const sessionListRecords = (payload) => {
284
+ if (Array.isArray(payload)) return payload;
285
+ if (payload && typeof payload === 'object' && Array.isArray(payload.data)) return payload.data;
286
+ return null;
274
287
  };
275
288
 
276
289
  export const registerOpenCodeProxy = (app, deps) => {
277
290
  const {
278
291
  fs,
279
- os,
280
- path,
281
292
  OPEN_CODE_READY_GRACE_MS,
282
293
  LONG_REQUEST_TIMEOUT_MS,
283
294
  getRuntime,
@@ -289,8 +300,91 @@ export const registerOpenCodeProxy = (app, deps) => {
289
300
  getSseUpstreamStallTimeoutMs = () => SSE_UPSTREAM_STALL_TIMEOUT_MS,
290
301
  readWorktreeBootstrapStatus = getWorktreeBootstrapStatus,
291
302
  WORKTREE_READY_TIMEOUT_MS = 5 * 60 * 1000,
303
+ // OpenCode 2.x has no archive route, so archive state is OpenChamber's own
304
+ // and the proxy folds it onto the sessions it serves (`time.archived`).
305
+ // Session metadata lives on OpenCode's record; the proxy lays over only
306
+ // the entries an older OpenChamber left in the legacy file until they are
307
+ // migrated.
308
+ getArchivedSessions = null,
309
+ getStoredSessionMetadata = null,
292
310
  } = deps;
293
311
 
312
+ /**
313
+ * `{ [sessionID]: archivedAt }` for the current instance, or `null` when the
314
+ * store cannot answer. `null` means "unknown", and an unknown answer leaves
315
+ * the upstream record untouched — never rewrites a session as un-archived.
316
+ */
317
+ const readArchivedSessions = async () => {
318
+ if (typeof getArchivedSessions !== 'function') return null;
319
+ try {
320
+ const archived = await getArchivedSessions();
321
+ return archived && typeof archived === 'object' ? archived : null;
322
+ } catch (error) {
323
+ console.warn('[proxy] archive state unavailable:', error?.message ?? error);
324
+ return null;
325
+ }
326
+ };
327
+
328
+ /**
329
+ * `{ [sessionID]: metadata }` still waiting to be migrated to OpenCode, or
330
+ * `null` when the store cannot answer. `null` means "unknown", and an
331
+ * unknown answer leaves the upstream record untouched.
332
+ */
333
+ const readStoredSessionMetadata = async () => {
334
+ if (typeof getStoredSessionMetadata !== 'function') return null;
335
+ try {
336
+ const stored = await getStoredSessionMetadata();
337
+ // Nothing left to migrate is the normal state: skip the rewrite entirely.
338
+ return stored && typeof stored === 'object' && Object.keys(stored).length > 0 ? stored : null;
339
+ } catch (error) {
340
+ console.warn('[proxy] session metadata unavailable:', error?.message ?? error);
341
+ return null;
342
+ }
343
+ };
344
+
345
+ // A number archives, `null` is an explicit unarchive (drops the stamp OpenCode
346
+ // still carries for a session migrated from v1), and a session the file does
347
+ // not mention keeps whatever OpenCode says.
348
+ const withArchivedAt = (session, archived) => {
349
+ if (!session || typeof session !== 'object' || typeof session.id !== 'string') return session;
350
+ if (!Object.prototype.hasOwnProperty.call(archived, session.id)) return session;
351
+ const archivedAt = archived[session.id];
352
+ const time = session.time && typeof session.time === 'object' ? session.time : {};
353
+ if (typeof archivedAt === 'number') {
354
+ return { ...session, time: { ...time, archived: archivedAt } };
355
+ }
356
+ if (!('archived' in time)) return session;
357
+ const { archived: _dropped, ...rest } = time;
358
+ return { ...session, time: rest };
359
+ };
360
+
361
+ /**
362
+ * A legacy entry is the newest metadata its session has, including {}, so it
363
+ * replaces the upstream record until migration pushes it there.
364
+ */
365
+ const withStoredMetadata = (session, stored) => {
366
+ if (!session || typeof session !== 'object' || typeof session.id !== 'string') return session;
367
+ const ours = stored[session.id];
368
+ if (!ours || typeof ours !== 'object' || Array.isArray(ours)) return session;
369
+ return { ...session, metadata: ours };
370
+ };
371
+
372
+ const overlaySession = (session, archived, stored) => {
373
+ let result = session;
374
+ if (archived) result = withArchivedAt(result, archived);
375
+ if (stored) result = withStoredMetadata(result, stored);
376
+ return result;
377
+ };
378
+
379
+ const overlayOwnedStateOnList = async (payload) => {
380
+ const records = sessionListRecords(payload);
381
+ if (!records) return payload;
382
+ const [archived, stored] = await Promise.all([readArchivedSessions(), readStoredSessionMetadata()]);
383
+ if (!archived && !stored) return payload;
384
+ const overlaid = records.map((session) => overlaySession(session, archived, stored));
385
+ return Array.isArray(payload) ? overlaid : { ...payload, data: overlaid };
386
+ };
387
+
294
388
  if (app.get('opencodeProxyConfigured')) {
295
389
  return;
296
390
  }
@@ -412,15 +506,9 @@ export const registerOpenCodeProxy = (app, deps) => {
412
506
  const PROXY_REQUEST_TIMEOUT_MS = normalizeProxyTimeout(LONG_REQUEST_TIMEOUT_MS);
413
507
  const PROXY_TIMEOUT_MARKER = Symbol('openchamberProxyTimedOut');
414
508
 
415
- // A provider OAuth callback blocks upstream for as long as the user takes to
416
- // sign in in their browser (device-code polling, or a loopback redirect), so
417
- // it cannot share the ordinary request deadline. Bounded by the shortest
418
- // upstream expiry we know of — GitHub device codes last ~15 minutes.
419
- const INTERACTIVE_OAUTH_TIMEOUT_MS = 15 * 60 * 1000;
420
- const INTERACTIVE_OAUTH_PATH = /^\/provider\/[^/]+\/oauth\/callback\/?$/;
421
-
422
- const isInteractiveOAuthCallback = (req) =>
423
- req.method === 'POST' && INTERACTIVE_OAUTH_PATH.test(req.path);
509
+ // OpenCode 2.x runs provider connection through `/api/integration/*`, whose
510
+ // OAuth steps return immediately and are polled, so no route needs a deadline
511
+ // longer than the ordinary one any more.
424
512
 
425
513
  const isProxyTimeoutError = (error) => {
426
514
  const code = typeof error?.code === 'string' ? error.code : '';
@@ -440,10 +528,6 @@ export const registerOpenCodeProxy = (app, deps) => {
440
528
  };
441
529
 
442
530
  const applyProxyResponseDeadline = (req, res, next) => {
443
- if (isInteractiveOAuthCallback(req)) {
444
- return next();
445
- }
446
-
447
531
  const timeout = setTimeout(() => {
448
532
  req[PROXY_TIMEOUT_MARKER] = true;
449
533
  if (sendProxyErrorResponse(res, 504)) {
@@ -475,7 +559,7 @@ export const registerOpenCodeProxy = (app, deps) => {
475
559
  const requestUrl = typeof req.originalUrl === 'string' && req.originalUrl.length > 0
476
560
  ? req.originalUrl
477
561
  : (typeof req.url === 'string' ? req.url : '');
478
- const upstreamPath = requestUrl.startsWith('/api') ? requestUrl.slice(4) || '/' : requestUrl;
562
+ const upstreamPath = requestUrl;
479
563
  const headers = normalizeForwardedDirectoryHeaders(
480
564
  collectForwardProxyHeaders(req.headers, getOpenCodeAuthHeaders())
481
565
  );
@@ -653,8 +737,9 @@ export const registerOpenCodeProxy = (app, deps) => {
653
737
  const requestUrl = typeof req.originalUrl === 'string' && req.originalUrl.length > 0
654
738
  ? req.originalUrl
655
739
  : (typeof req.url === 'string' ? req.url : '');
656
- const upstreamPathRaw = requestUrl.startsWith('/api') ? requestUrl.slice(4) || '/' : requestUrl;
657
- return canonicalizeDirectoryQuery(upstreamPathRaw);
740
+ // OpenCode 2.x serves everything under `/api/*` itself, so the upstream
741
+ // path is the request path — nothing is stripped.
742
+ return canonicalizeDirectoryQuery(requestUrl);
658
743
  };
659
744
 
660
745
  const forwardSanitizedSessionListRequest = async (req, res, next, logLabel) => {
@@ -671,14 +756,14 @@ export const registerOpenCodeProxy = (app, deps) => {
671
756
  return;
672
757
  }
673
758
 
674
- if (result.parseError || !Array.isArray(result.payload)) {
759
+ if (result.parseError || !sessionListRecords(result.payload)) {
675
760
  res.setHeader('content-type', result.contentType);
676
761
  res.end(result.bodyText);
677
762
  return;
678
763
  }
679
764
 
680
765
  res.setHeader('content-type', result.contentType);
681
- res.json(sanitizeSessionListPayload(result.payload));
766
+ res.json(await overlayOwnedStateOnList(sanitizeSessionListPayload(result.payload)));
682
767
  } catch (error) {
683
768
  if (isAbortError(error)) {
684
769
  return;
@@ -719,7 +804,7 @@ export const registerOpenCodeProxy = (app, deps) => {
719
804
  const classifyReadinessRoute = (requestPath) => {
720
805
  if (/^\/session\/[^/]+\/message(?:\/|$)/.test(requestPath)) return 'session-messages';
721
806
  if (requestPath === '/session' || requestPath.startsWith('/session/')) return 'session';
722
- if (requestPath === '/event' || requestPath === '/global/event') return 'events';
807
+ if (requestPath === '/event') return 'events';
723
808
  return 'other';
724
809
  };
725
810
 
@@ -808,103 +893,68 @@ export const registerOpenCodeProxy = (app, deps) => {
808
893
  }
809
894
  });
810
895
 
811
- // Windows: session merge for cross-directory session listing
812
- if (process.platform === 'win32') {
813
- app.get('/api/session', async (req, res, next) => {
814
- const rawUrl = req.originalUrl || req.url || '';
815
- if (rawUrl.includes('directory=')) return next();
816
-
817
- const fetchWindowsSessionList = async (sessionPath) => {
818
- const result = await fetchSessionListPayload(sessionPath, { req, timeoutMs: 10000 });
819
- if (!result.upstream.ok || !Array.isArray(result.payload)) return null;
820
- return sanitizeSessionListPayload(result.payload);
821
- };
822
-
823
- try {
824
- const globalSessions = await fetchWindowsSessionList('/session').catch((error) => {
825
- console.log(`[SessionMerge] Global session list failed: ${error.message}`);
826
- return null;
827
- });
896
+ // V2 lists sessions across directories on every platform and owns pagination.
897
+ app.get('/api/session', (req, res, next) => {
898
+ return forwardSanitizedSessionListRequest(req, res, next, 'session.list');
899
+ });
828
900
 
829
- const settingsPath = path.join(os.homedir(), '.config', 'openchamber', 'settings.json');
830
- let projectDirs = [];
831
- try {
832
- const settingsRaw = fs.readFileSync(settingsPath, 'utf8');
833
- const settings = JSON.parse(settingsRaw);
834
- projectDirs = (settings.projects || [])
835
- .map((project) => (typeof project?.path === 'string' ? project.path.trim() : ''))
836
- .filter(Boolean);
837
- } catch {
838
- }
901
+ // One session: the same overlay, so a detail read agrees with the list it
902
+ // came from. Everything else about the record is forwarded untouched.
903
+ app.get('/api/session/:sessionID', async (req, res, next) => {
904
+ if (typeof getArchivedSessions !== 'function' && typeof getStoredSessionMetadata !== 'function') return next();
905
+ try {
906
+ const upstreamPath = await getRequestUpstreamPath(req);
907
+ const result = await fetchSessionListPayload(upstreamPath, { req });
839
908
 
840
- const seen = new Set(
841
- (globalSessions || [])
842
- .map((session) => (session && typeof session.id === 'string' ? session.id : null))
843
- .filter((id) => typeof id === 'string')
844
- );
845
- const extraSessions = [];
846
- let successfulProjectReads = 0;
847
- for (const dir of projectDirs) {
848
- const candidates = Array.from(new Set([
849
- dir,
850
- dir.replace(/\\/g, '/'),
851
- dir.replace(/\//g, '\\'),
852
- ]));
853
- for (const candidateDir of candidates) {
854
- const encoded = encodeURIComponent(candidateDir);
855
- try {
856
- const dirSessions = await fetchWindowsSessionList(`/session?directory=${encoded}`);
857
- if (dirSessions) {
858
- successfulProjectReads += 1;
859
- }
860
- for (const session of dirSessions || []) {
861
- const id = session && typeof session.id === 'string' ? session.id : null;
862
- if (id && !seen.has(id)) {
863
- seen.add(id);
864
- extraSessions.push(session);
865
- }
866
- }
867
- } catch {
868
- }
869
- }
870
- }
909
+ res.status(result.upstream.status);
910
+ applyForwardProxyResponseHeaders(result.upstream.headers, res);
911
+ res.setHeader('content-type', result.contentType);
871
912
 
872
- if (!globalSessions && successfulProjectReads === 0) {
873
- return res.status(504).json({ error: 'OpenCode session list timed out' });
874
- }
913
+ const record = result.isJson && !result.parseError ? result.payload : null;
914
+ const session = record && typeof record === 'object' && !Array.isArray(record)
915
+ ? (record.data && typeof record.data === 'object' ? record.data : record)
916
+ : null;
917
+ if (!session || typeof session.id !== 'string') {
918
+ res.end(result.bodyText);
919
+ return;
920
+ }
875
921
 
876
- const merged = [...(globalSessions || []), ...extraSessions];
877
- merged.sort((a, b) => {
878
- const aTime = a && typeof a.time_updated === 'number' ? a.time_updated : 0;
879
- const bTime = b && typeof b.time_updated === 'number' ? b.time_updated : 0;
880
- return bTime - aTime;
881
- });
882
- console.log(`[SessionMerge] ${globalSessions?.length || 0} global + ${extraSessions.length} extra = ${merged.length} total`);
883
- return res.json(sanitizeSessionListPayload(merged));
884
- } catch (error) {
885
- console.log(`[SessionMerge] Error: ${error.message}`);
886
- return res.status(500).json({ error: error.message || 'Failed to merge Windows sessions' });
922
+ const [archived, stored] = await Promise.all([readArchivedSessions(), readStoredSessionMetadata()]);
923
+ if (!archived && !stored) {
924
+ res.end(result.bodyText);
925
+ return;
887
926
  }
888
- });
889
- }
890
927
 
891
- app.get('/api/session', (req, res, next) => {
892
- return forwardSanitizedSessionListRequest(req, res, next, 'session.list');
928
+ const overlaid = overlaySession(session, archived, stored);
929
+ res.json(record.data && typeof record.data === 'object' ? { ...record, data: overlaid } : overlaid);
930
+ } catch (error) {
931
+ if (isAbortError(error)) return;
932
+ console.error('[proxy] OpenCode session.get proxy error:', error?.message ?? error);
933
+ if (!res.headersSent) {
934
+ res.status(503).json({ error: 'OpenCode service unavailable' });
935
+ return;
936
+ }
937
+ next(error);
938
+ }
893
939
  });
894
940
 
895
- app.get('/api/global/event', forwardSseRequest);
896
- app.get('/api/event', forwardSseRequest);
897
-
898
- app.get('/api/experimental/session', (req, res, next) => {
899
- return forwardSanitizedSessionListRequest(req, res, next, 'experimental.session');
941
+ // v2 has one event stream. `/api/global/event` stays as an alias so a client
942
+ // that has not reloaded yet keeps working; both reach upstream `/api/event`.
943
+ app.get('/api/global/event', (req, res, next) => {
944
+ req.url = req.url.replace('/api/global/event', '/api/event');
945
+ if (typeof req.originalUrl === 'string') {
946
+ req.originalUrl = req.originalUrl.replace('/api/global/event', '/api/event');
947
+ }
948
+ return forwardSseRequest(req, res, next);
900
949
  });
950
+ app.get('/api/event', forwardSseRequest);
901
951
 
902
952
  // Generic proxy for non-SSE OpenCode API routes.
903
953
  // The agent is exposed as a getter so its class is resolved per request, not
904
954
  // at registration: the proxy is registered before OpenCode bootstraps, so an
905
955
  // https target configured via OPENCODE_HOST is not yet visible here. Agents
906
956
  // are memoized per scheme, so this is still one shared pool per scheme across
907
- // `apiProxy` and `interactiveOAuthProxy`.
957
+ // `apiProxy`.
908
958
  const resolveOpenCodeProxyAgent = createOpenCodeProxyAgentResolver(resolveProxyTarget);
909
959
 
910
960
  const createApiProxy = (timeoutMs) => createProxyMiddleware({
@@ -913,9 +963,12 @@ export const registerOpenCodeProxy = (app, deps) => {
913
963
  return resolveOpenCodeProxyAgent();
914
964
  },
915
965
  changeOrigin: true,
916
- pathRewrite: { '^/api': '' },
917
966
  timeout: timeoutMs,
918
967
  proxyTimeout: timeoutMs,
968
+ // The proxy is mounted on `/api`, so Express has already stripped that
969
+ // prefix by the time the middleware sees the request. OpenCode 2.x serves
970
+ // everything under `/api/*` itself, so put it back.
971
+ pathRewrite: (proxiedPath) => `/api${proxiedPath === '/' ? '' : proxiedPath}`,
919
972
  // Dynamic target — port can change after restart
920
973
  router: () => resolveProxyTarget(),
921
974
  on: {
@@ -963,7 +1016,6 @@ export const registerOpenCodeProxy = (app, deps) => {
963
1016
  });
964
1017
 
965
1018
  const apiProxy = createApiProxy(PROXY_REQUEST_TIMEOUT_MS);
966
- const interactiveOAuthProxy = createApiProxy(INTERACTIVE_OAUTH_TIMEOUT_MS);
967
1019
 
968
1020
  // Best-effort fallback for stale clients still sending symlink paths.
969
1021
  // Settings and project selection normalize at source; this cached async path
@@ -981,10 +1033,8 @@ export const registerOpenCodeProxy = (app, deps) => {
981
1033
  });
982
1034
 
983
1035
  app.use('/api', applyProxyResponseDeadline);
984
- app.post('/api/provider/:providerID/oauth/callback', interactiveOAuthProxy);
985
- // OpenCode's native MCP OAuth flow: the request blocks until the user
986
- // finishes authorization in the browser (up to OpenCode's 5-minute callback
987
- // timeout), so it needs the interactive-OAuth deadline, not the default one.
988
- app.post('/api/mcp/:name/auth/authenticate', interactiveOAuthProxy);
1036
+ // v1's interactive provider/MCP OAuth callbacks are gone: v2 runs provider
1037
+ // connection through `/api/integration/*`, which answers immediately and
1038
+ // needs no special deadline.
989
1039
  app.use('/api', apiProxy);
990
1040
  };
@@ -101,7 +101,7 @@ export const registerPwaManifestRoute = (app, dependencies) => {
101
101
  return `?directory=${encodeURIComponent(preparedDirectory)}`;
102
102
  })();
103
103
 
104
- const response = await fetch(buildOpenCodeUrl(`/session${query}`, ''), {
104
+ const response = await fetch(buildOpenCodeUrl(`/api/session${query}`, ''), {
105
105
  method: 'GET',
106
106
  headers: {
107
107
  Accept: 'application/json',
@@ -114,7 +114,9 @@ export const registerPwaManifestRoute = (app, dependencies) => {
114
114
  return [];
115
115
  }
116
116
 
117
- const payload = await response.json().catch(() => null);
117
+ // v2 pages the session list as `{ data, cursor }`.
118
+ const body = await response.json().catch(() => null);
119
+ const payload = Array.isArray(body) ? body : body?.data;
118
120
  return Array.isArray(payload) ? payload : [];
119
121
  };
120
122
 
@@ -41,9 +41,10 @@ describe('PWA manifest route', () => {
41
41
  time: { updated: 2 },
42
42
  },
43
43
  ];
44
+ // v2 pages the session list as `{ data, cursor }`.
44
45
  return {
45
46
  ok: true,
46
- json: async () => sessions,
47
+ json: async () => ({ data: sessions, cursor: {} }),
47
48
  };
48
49
  };
49
50
 
@@ -118,7 +119,7 @@ describe('PWA manifest route', () => {
118
119
  await handler({ query: {} }, res);
119
120
 
120
121
  const manifest = JSON.parse(res.body);
121
- expect(fetchCalls).toEqual(['/session?directory=%2F']);
122
+ expect(fetchCalls).toEqual(['/api/session?directory=%2F']);
122
123
  expect(manifest.shortcuts).toContainEqual({
123
124
  name: 'Root child',
124
125
  short_name: 'Root child',
@@ -0,0 +1,14 @@
1
+ /**
2
+ * OpenCode 2.x wraps `/api/*` answers in an envelope, and not always the same
3
+ * one: a single record (`GET /api/session/:id`, one message) comes back as
4
+ * `{ data }`, some routes (`/api/command`) as `{ location, data }`, and pages
5
+ * as `{ data, cursor }`. Records and plain lists are unwrapped; a page keeps
6
+ * its envelope because callers read its cursor.
7
+ *
8
+ * Unwrapping only when `location` was present left record envelopes in place,
9
+ * so a session's `parentID` and a message's `id` read as missing.
10
+ */
11
+ export const unwrapOpenCodeResponse = (body) => {
12
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return body;
13
+ return 'data' in body && !('cursor' in body) ? body.data : body;
14
+ };
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { unwrapOpenCodeResponse } from './response-envelope.js';
4
+
5
+ describe('unwrapOpenCodeResponse', () => {
6
+ it('unwraps a single record sent as { data } alone', () => {
7
+ expect(unwrapOpenCodeResponse({ data: { id: 'ses_1', parentID: 'ses_0' } })).toEqual({ id: 'ses_1', parentID: 'ses_0' });
8
+ });
9
+
10
+ it('unwraps a { location, data } answer', () => {
11
+ expect(unwrapOpenCodeResponse({ location: { directory: '/repo' }, data: [{ name: 'review' }] })).toEqual([{ name: 'review' }]);
12
+ });
13
+
14
+ it('keeps a page envelope so its cursor stays readable', () => {
15
+ const page = { data: [{ id: 'ses_1' }], cursor: { next: 'c1' } };
16
+ expect(unwrapOpenCodeResponse(page)).toBe(page);
17
+ });
18
+
19
+ it('passes through what is not an envelope', () => {
20
+ expect(unwrapOpenCodeResponse(null)).toBe(null);
21
+ expect(unwrapOpenCodeResponse([1])).toEqual([1]);
22
+ });
23
+ });