@openchambery/web 1.19.22-beta.1 → 2.0.0-beta.10

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 (328) hide show
  1. package/README.md +13 -7
  2. package/bin/cli-opencode.test.js +208 -0
  3. package/bin/cli.js +62 -9
  4. package/bin/lib/cli-startup.js +105 -2
  5. package/bin/lib/cli-startup.test.js +179 -0
  6. package/dist/assets/{AgentAvatar-CRaBJXKs.js → AgentAvatar-CzTtVMnI.js} +1 -1
  7. package/dist/assets/AssistantView-Cg1qQvVe.js +3 -0
  8. package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-hOi72Wdx.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-DCqrQkkz.js} +1 -1
  10. package/dist/assets/Icon-B8fSTLdP.js +1 -0
  11. package/dist/assets/MarkdownRenderer-Cw74oVxV.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-YM7iRmzK.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-BDy_oGJQ.js +3 -0
  14. package/dist/assets/MobileDetailNavigation-iX_hb_Xt.js +1 -0
  15. package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-CAMUupwR.js} +2 -2
  16. package/dist/assets/MobileSurface-CHbdPcLH.js +2 -0
  17. package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-Ci1ODFFX.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-O4cGdz5p.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-CRfGtcmG.js +2 -0
  20. package/dist/assets/SettingsView-Dg8BtTV1.js +25 -0
  21. package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-B8d8qIQ7.js} +1 -1
  22. package/dist/assets/TerminalView-CMiw3s_c.js +8 -0
  23. package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-ByaQYRTp.js} +2 -2
  24. package/dist/assets/appThemeRegistry-BGGHio8T.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-CI7Q-Zw_.js → button-DIIrsMjW.js} +1 -1
  27. package/dist/assets/{collapsible-D5Phaykh.js → collapsible-CnvUeYst.js} +1 -1
  28. package/dist/assets/{context-menu-CubSDW2g.js → context-menu-BGBUpZEQ.js} +1 -1
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-JGFqhlaM.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-CHEwrI93.js} +1 -1
  31. package/dist/assets/{diagram-DdLO8HZ9.js → diagram-rvn7_qBM.js} +1 -1
  32. package/dist/assets/{dialog-B6pnIYmp.js → dialog-CQ63sc9E.js} +1 -1
  33. package/dist/assets/es-Dsmc0A_3.js +15 -0
  34. package/dist/assets/fileMentionTouchSelection-BzzcWJCi.js +3 -0
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-DN6gbLhM.js} +1 -1
  36. package/dist/assets/fr-C_ZlZDI2.js +13 -0
  37. package/dist/assets/gitApi-BF8vuoZc.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-BMztRu0c.js} +1 -1
  39. package/dist/assets/{input-D-__fwRi.js → input-BVu-iykl.js} +1 -1
  40. package/dist/assets/insertionBoundaries-EcAfGT2D.js +1 -0
  41. package/dist/assets/ja-BqqTaWTi.js +15 -0
  42. package/dist/assets/ko-DP2XPM37.js +15 -0
  43. package/dist/assets/main-B7t9lavz.js +2 -0
  44. package/dist/assets/main-D2BzpnUt.css +1 -0
  45. package/dist/assets/main-DWP-prNB.js +169 -0
  46. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  47. package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-D9-B1wdY.js} +1 -1
  48. package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
  49. package/dist/assets/mcpQueries-CfXrAkuU.js +1 -0
  50. package/dist/assets/miniChat-DgOrEQbN.js +2 -0
  51. package/dist/assets/mobile-Co_ZOHkZ.js +2 -0
  52. package/dist/assets/multirun-DelyUO6k.js +4 -0
  53. package/dist/assets/persistence-D0y7226-.js +3 -0
  54. package/dist/assets/pl-BCf5uLlK.js +15 -0
  55. package/dist/assets/platform-CurqO28D.js +6 -0
  56. package/dist/assets/{projectMeta-S1hJ7oBp.js → projectMeta-CPElzJ0n.js} +2 -2
  57. package/dist/assets/pt-BR-W09Q2pvn.js +15 -0
  58. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  59. package/dist/assets/renderElectronMiniChatApp-pOE93w86.js +2 -0
  60. package/dist/assets/renderMobileApp-F7ZSLcw8.js +28 -0
  61. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  62. package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-DSS0KxG3.js} +1 -1
  63. package/dist/assets/runtimeEndpointReset-x2Wgx57R.js +149 -0
  64. package/dist/assets/sessionChangeSummary-BUGSYcHp.js +2 -0
  65. package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-Bss8M20i.js} +1 -1
  66. package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-CE7W7lJ9.js} +1 -1
  67. package/dist/assets/src-lS1keDVV.css +2 -0
  68. package/dist/assets/store-jaP79mqI.js +15 -0
  69. package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-mDR2jXTo.js} +1 -1
  70. package/dist/assets/textarea-XD2oX_Ee.js +1 -0
  71. package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-DYeQ_Hvy.js} +1 -1
  72. package/dist/assets/uk-BgFzz8m2.js +15 -0
  73. package/dist/assets/{url-_z7evzdK.js → url-CGbJbiJq.js} +1 -1
  74. package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-BAWHZ-t0.js} +64 -45
  75. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
  76. package/dist/assets/useDesktopWindowControlsLayout-hN_nLb7T.js +1 -0
  77. package/dist/assets/useEffectiveDirectory-Do9DQ453.js +1 -0
  78. package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-Bjx8zkGM.js} +1 -1
  79. package/dist/assets/useMobileNavigationStore-BsY_gi_K.js +414 -0
  80. package/dist/assets/useSessionAutoCleanup-C4g5KmDf.js +2 -0
  81. package/dist/assets/useUIStore-syVmbuDA.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-B5QXxBok.js} +2 -2
  83. package/dist/assets/vendor-opencode-client-C12gfjag.js +12 -0
  84. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
  85. package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
  86. package/dist/assets/zh-CN-BhqSLgUY.js +15 -0
  87. package/dist/assets/zh-TW-BsaBgY-p.js +15 -0
  88. package/dist/index.html +12 -12
  89. package/dist/mini-chat.html +12 -12
  90. package/dist/mobile.html +12 -12
  91. package/package.json +3 -2
  92. package/server/index.js +423 -20
  93. package/server/lib/assistants/DOCUMENTATION.md +26 -21
  94. package/server/lib/assistants/assign.js +2 -1
  95. package/server/lib/assistants/contact-store.js +37 -19
  96. package/server/lib/assistants/contracts.js +11 -1
  97. package/server/lib/assistants/projection-history.test.js +454 -0
  98. package/server/lib/assistants/routes.js +11 -5
  99. package/server/lib/assistants/service.js +850 -294
  100. package/server/lib/assistants/service.test.js +588 -274
  101. package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
  102. package/server/lib/browser-provider/contract.js +312 -0
  103. package/server/lib/browser-provider/host.js +421 -0
  104. package/server/lib/browser-provider/host.test.js +304 -0
  105. package/server/lib/browser-provider/routes.js +122 -0
  106. package/server/lib/browser-provider/routes.test.js +120 -0
  107. package/server/lib/browser-provider/selection-store.js +43 -0
  108. package/server/lib/browser-provider/selection-store.test.js +35 -0
  109. package/server/lib/conversations/DOCUMENTATION.md +5 -5
  110. package/server/lib/conversations/service.js +87 -57
  111. package/server/lib/conversations/service.test.js +132 -100
  112. package/server/lib/conversations/validation.js +11 -1
  113. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  114. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  115. package/server/lib/event-stream/global-hub.js +51 -5
  116. package/server/lib/event-stream/reasoning-projection.js +16 -1
  117. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  118. package/server/lib/event-stream/runtime.js +57 -20
  119. package/server/lib/event-stream/runtime.test.js +112 -0
  120. package/server/lib/llm/DOCUMENTATION.md +95 -84
  121. package/server/lib/llm/catalog.js +111 -45
  122. package/server/lib/llm/catalog.test.js +121 -59
  123. package/server/lib/llm/completions.js +41 -12
  124. package/server/lib/llm/completions.test.js +45 -4
  125. package/server/lib/llm/generate.js +516 -453
  126. package/server/lib/llm/generate.test.js +705 -484
  127. package/server/lib/llm/routes.js +8 -4
  128. package/server/lib/message-queue/DOCUMENTATION.md +5 -5
  129. package/server/lib/message-queue/opencode-adapter.js +191 -104
  130. package/server/lib/message-queue/opencode-adapter.test.js +328 -58
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/message-queue/runtime.test.js +6 -6
  133. package/server/lib/message-queue/worker.js +7 -1
  134. package/server/lib/notifications/DOCUMENTATION.md +3 -1
  135. package/server/lib/notifications/runtime.js +133 -31
  136. package/server/lib/notifications/runtime.test.js +103 -0
  137. package/server/lib/notifications/template-runtime.js +130 -38
  138. package/server/lib/notifications/template-runtime.test.js +96 -0
  139. package/server/lib/opencode/DOCUMENTATION.md +111 -16
  140. package/server/lib/opencode/agent-document.d.ts +28 -0
  141. package/server/lib/opencode/agent-document.js +280 -0
  142. package/server/lib/opencode/agent-document.test.js +137 -0
  143. package/server/lib/opencode/agents.js +96 -0
  144. package/server/lib/opencode/auth-state-runtime.js +5 -0
  145. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  146. package/server/lib/opencode/config-entity-routes.js +117 -103
  147. package/server/lib/opencode/config-entity-routes.test.js +148 -10
  148. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  149. package/server/lib/opencode/ensure-cli.d.ts +8 -0
  150. package/server/lib/opencode/ensure-cli.js +238 -0
  151. package/server/lib/opencode/ensure-cli.test.js +119 -0
  152. package/server/lib/opencode/env-runtime.js +208 -111
  153. package/server/lib/opencode/env-runtime.test.js +211 -14
  154. package/server/lib/opencode/feature-routes-runtime.js +90 -5
  155. package/server/lib/opencode/feature-routes-runtime.test.js +25 -0
  156. package/server/lib/opencode/http-server-connections.js +98 -0
  157. package/server/lib/opencode/http-server-connections.test.js +321 -0
  158. package/server/lib/opencode/lifecycle.js +539 -95
  159. package/server/lib/opencode/lifecycle.test.js +698 -6
  160. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  161. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  162. package/server/lib/opencode/network-runtime.js +28 -16
  163. package/server/lib/opencode/network-runtime.test.js +68 -4
  164. package/server/lib/opencode/openchamber-routes.js +7 -1
  165. package/server/lib/opencode/opencode2-pin.js +139 -0
  166. package/server/lib/opencode/opencode2-pin.test.js +88 -0
  167. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
  168. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  169. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  170. package/server/lib/opencode/plugin-compatibility.js +15 -0
  171. package/server/lib/opencode/plugin-compatibility.test.js +21 -0
  172. package/server/lib/opencode/plugin-routes.js +3 -18
  173. package/server/lib/opencode/plugin-routes.test.js +9 -9
  174. package/server/lib/opencode/plugins.js +4 -3
  175. package/server/lib/opencode/plugins.test.js +12 -0
  176. package/server/lib/opencode/provider-catalog.js +58 -12
  177. package/server/lib/opencode/provider-catalog.test.js +48 -0
  178. package/server/lib/opencode/proxy.js +373 -28
  179. package/server/lib/opencode/proxy.test.js +353 -0
  180. package/server/lib/opencode/routes.js +333 -76
  181. package/server/lib/opencode/routes.test.js +315 -50
  182. package/server/lib/opencode/runtime-contract.js +401 -0
  183. package/server/lib/opencode/runtime-contract.test.js +214 -0
  184. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  185. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  186. package/server/lib/opencode/server-utils-runtime.js +35 -0
  187. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  188. package/server/lib/opencode/shared-service.js +147 -0
  189. package/server/lib/opencode/shared-service.test.js +93 -0
  190. package/server/lib/opencode/shared.js +3 -3
  191. package/server/lib/opencode/shutdown-runtime.js +15 -6
  192. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  193. package/server/lib/opencode/skill-routes.js +26 -40
  194. package/server/lib/opencode/skill-routes.test.js +71 -7
  195. package/server/lib/opencode/skills.test.js +15 -0
  196. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  197. package/server/lib/opencode/static-routes-runtime.js +3 -1
  198. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  199. package/server/lib/opencode/upgrade-screen.d.ts +30 -0
  200. package/server/lib/opencode/upgrade-screen.js +299 -0
  201. package/server/lib/opencode/upgrade-screen.test.js +196 -0
  202. package/server/lib/opencode/v1-migration-gate.js +157 -0
  203. package/server/lib/opencode/v1-migration-gate.test.js +111 -0
  204. package/server/lib/opencode/v2-client.js +60 -0
  205. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  206. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  207. package/server/lib/opencode/watcher.js +2 -1
  208. package/server/lib/opencode/watcher.test.js +1 -1
  209. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  210. package/server/lib/permission-auto-accept/runtime.js +14 -7
  211. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  212. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  213. package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
  214. package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
  215. package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
  216. package/server/lib/scheduled-tasks/routes.js +27 -0
  217. package/server/lib/scheduled-tasks/routes.test.js +94 -0
  218. package/server/lib/scheduled-tasks/runtime.js +381 -310
  219. package/server/lib/scheduled-tasks/runtime.test.js +600 -309
  220. package/server/lib/session-goal/DOCUMENTATION.md +173 -22
  221. package/server/lib/session-goal/capability.js +57 -0
  222. package/server/lib/session-goal/capability.test.js +33 -0
  223. package/server/lib/session-goal/routes.js +240 -2
  224. package/server/lib/session-goal/routes.test.js +211 -0
  225. package/server/lib/session-goal/runtime.js +1617 -113
  226. package/server/lib/session-goal/runtime.test.js +1500 -70
  227. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  228. package/server/lib/session-index/event-ingest.js +89 -16
  229. package/server/lib/session-index/event-ingest.test.js +73 -0
  230. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  231. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  232. package/server/lib/session-index/routes.js +63 -10
  233. package/server/lib/session-index/routes.test.js +8 -8
  234. package/server/lib/session-index/service.js +7 -1
  235. package/server/lib/session-index/sync-runtime.js +171 -41
  236. package/server/lib/session-index/sync-runtime.test.js +164 -9
  237. package/server/lib/session-metadata/DOCUMENTATION.md +96 -0
  238. package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
  239. package/server/lib/session-metadata/opencode-session-record.js +316 -0
  240. package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
  241. package/server/lib/session-metadata/routes.js +197 -0
  242. package/server/lib/session-metadata/routes.test.js +156 -0
  243. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  244. package/server/lib/session-metadata/session-archive.js +654 -0
  245. package/server/lib/session-metadata/session-archive.test.js +359 -0
  246. package/server/lib/session-metadata/session-metadata-store.d.ts +38 -0
  247. package/server/lib/session-metadata/session-metadata-store.js +422 -0
  248. package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
  249. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  250. package/server/lib/session-metadata/session-projection.js +279 -0
  251. package/server/lib/session-metadata/session-projection.test.js +64 -0
  252. package/server/lib/session-metadata/system-session.js +55 -0
  253. package/server/lib/session-metadata/system-session.test.js +55 -0
  254. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  255. package/server/lib/session-title/native-runtime.test.js +167 -0
  256. package/server/lib/session-title/runtime.js +69 -44
  257. package/server/lib/session-title/session-access.js +46 -0
  258. package/server/lib/session-title/session-access.test.js +91 -0
  259. package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
  260. package/server/lib/session-turn-pages/reconcile.service.js +1 -1
  261. package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
  262. package/server/lib/session-turn-pages/routes.js +123 -75
  263. package/server/lib/session-turn-pages/routes.test.js +566 -0
  264. package/server/lib/session-turn-pages/service.js +6 -2
  265. package/server/lib/session-turn-pages/service.test.js +34 -0
  266. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  267. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  268. package/server/lib/small-model/DOCUMENTATION.md +101 -146
  269. package/server/lib/small-model/catalog.js +84 -54
  270. package/server/lib/small-model/catalog.test.js +111 -9
  271. package/server/lib/small-model/custom-api.js +68 -0
  272. package/server/lib/small-model/custom-api.test.js +64 -0
  273. package/server/lib/small-model/index.js +125 -241
  274. package/server/lib/small-model/resolve.js +35 -120
  275. package/server/lib/small-model/resolve.test.js +20 -110
  276. package/server/lib/small-model/routes.js +1 -3
  277. package/server/lib/small-model/summary-settings.test.js +157 -253
  278. package/server/opencode-proxy.test.js +63 -12
  279. package/dist/assets/AssistantView-BUveAgc4.js +0 -3
  280. package/dist/assets/Icon-BgdfPhNs.js +0 -1
  281. package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
  282. package/dist/assets/MarkstreamRendererImpl-D2dsW5Lg.js +0 -3
  283. package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
  284. package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
  285. package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
  286. package/dist/assets/SettingsView-PkXIavr7.js +0 -25
  287. package/dist/assets/TerminalView-CDdzF4yi.js +0 -8
  288. package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
  289. package/dist/assets/es-DA3MqOo0.js +0 -15
  290. package/dist/assets/fileMentionTouchSelection-DNeiqns-.js +0 -4
  291. package/dist/assets/fr-Dbz0CdIA.js +0 -13
  292. package/dist/assets/gitApi-DXyC4MiV.js +0 -7
  293. package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
  294. package/dist/assets/ja-DumanVQA.js +0 -15
  295. package/dist/assets/ko-C0IkiUjQ.js +0 -15
  296. package/dist/assets/main-BPS5ylib.js +0 -2
  297. package/dist/assets/main-Cpd9VWYs.js +0 -169
  298. package/dist/assets/main-qSeeJRW1.css +0 -1
  299. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  300. package/dist/assets/markdownTheme-DOfGg-A7.js +0 -1
  301. package/dist/assets/miniChat-B8_KLWiY.js +0 -2
  302. package/dist/assets/mobile-CtktKrXn.js +0 -2
  303. package/dist/assets/multirun-CYfP9Cfm.js +0 -4
  304. package/dist/assets/persistence-Ce3AnpEr.js +0 -3
  305. package/dist/assets/pl-B9No6MQp.js +0 -15
  306. package/dist/assets/platform-0w_Q9_ji.js +0 -2
  307. package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
  308. package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
  309. package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
  310. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  311. package/dist/assets/runtimeEndpointReset-ChOxt-J7.js +0 -147
  312. package/dist/assets/sessionChangeSummary-NnxcqCTI.js +0 -2
  313. package/dist/assets/src-rS3zCcsC.css +0 -2
  314. package/dist/assets/store-5B_LDnLM.js +0 -15
  315. package/dist/assets/textarea-bHYxZtfH.js +0 -1
  316. package/dist/assets/uk-C_1KkqyP.js +0 -15
  317. package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
  318. package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
  319. package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
  320. package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
  321. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  322. package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
  323. package/dist/assets/zh-CN-uWILgBdt.js +0 -15
  324. package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
  325. package/server/lib/small-model/call.js +0 -427
  326. package/server/lib/small-model/call.test.js +0 -382
  327. package/server/lib/small-model/opencode-session.js +0 -339
  328. package/server/lib/small-model/opencode-session.test.js +0 -177
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Startup upgrade screen for OpenCode below 2.0.15.
3
+ *
4
+ * This module does not own process lifecycle. It classifies the version the
5
+ * existing startup path already detected, and installs through the owned-cache
6
+ * installer (`installPinnedOpenCode2Cli`) plus the caller's restart hook.
7
+ * Success is a verified running version, never a download that has not started.
8
+ */
9
+
10
+ import {
11
+ PINNED_OPENCODE2_VERSION,
12
+ isAcceptableOpenCode2HealthVersion,
13
+ isOpenCode1xVersion,
14
+ isOpenCode2VersionAtLeast,
15
+ npmPackageForOpenCode2,
16
+ } from './opencode2-pin.js';
17
+
18
+ /** Oldest OpenCode this OpenChamber build will enter a session against. */
19
+ export const REQUIRED_OPENCODE_VERSION = '2.0.15';
20
+
21
+ const normalizeVersion = (value) => {
22
+ if (typeof value !== 'string') return null;
23
+ const trimmed = value.trim().replace(/^v/i, '');
24
+ return trimmed || null;
25
+ };
26
+
27
+ /**
28
+ * Install target is the pin when the pin already satisfies the screen, otherwise
29
+ * the screen minimum. A pin still at 2.0.12 must not be what this screen installs.
30
+ * @param {string} [pin]
31
+ */
32
+ export function resolveUpgradeScreenTarget(pin = PINNED_OPENCODE2_VERSION) {
33
+ if (isOpenCode2VersionAtLeast(pin, REQUIRED_OPENCODE_VERSION)) return pin;
34
+ return REQUIRED_OPENCODE_VERSION;
35
+ }
36
+
37
+ /** 2.x at or above the screen minimum. A future major is not this contract. */
38
+ function isUpgradeScreenSatisfied(version) {
39
+ const normalized = normalizeVersion(version);
40
+ if (!normalized || !isAcceptableOpenCode2HealthVersion(normalized)) return false;
41
+ return isOpenCode2VersionAtLeast(normalized, REQUIRED_OPENCODE_VERSION);
42
+ }
43
+
44
+ function isOlderThanUpgradeScreenMinimum(version) {
45
+ const normalized = normalizeVersion(version);
46
+ if (!normalized) return false;
47
+ if (isOpenCode1xVersion(normalized)) return true;
48
+ if (!isAcceptableOpenCode2HealthVersion(normalized)) return false;
49
+ return !isOpenCode2VersionAtLeast(normalized, REQUIRED_OPENCODE_VERSION);
50
+ }
51
+
52
+ /**
53
+ * @param {string | null | undefined} ownership
54
+ * @returns {'managed' | 'external' | 'bundled'}
55
+ */
56
+ function classifyUpgradeScreenInstallation(ownership) {
57
+ if (ownership === 'external-serve') return 'external';
58
+ if (ownership === 'bundled') return 'bundled';
59
+ return 'managed';
60
+ }
61
+
62
+ export function platformSupportsUpgradeScreenInstall(platform = process.platform, arch = process.arch) {
63
+ try {
64
+ npmPackageForOpenCode2(platform, arch);
65
+ return true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ const pickDetectedVersion = (serveVersion, cliVersion) => {
72
+ const serve = normalizeVersion(serveVersion);
73
+ const cli = normalizeVersion(cliVersion);
74
+ if (serve && !isUpgradeScreenSatisfied(serve)) return serve;
75
+ if (serve) return serve;
76
+ return cli;
77
+ };
78
+
79
+ /**
80
+ * @param {{
81
+ * version?: string | null,
82
+ * installation?: 'managed' | 'external' | 'bundled',
83
+ * platformCanInstall?: boolean,
84
+ * guidance?: string | null,
85
+ * pin?: string,
86
+ * }} input
87
+ */
88
+ export function describeUpgradeScreen(input = {}) {
89
+ const version = normalizeVersion(input.version);
90
+ const installation = input.installation === 'external' || input.installation === 'bundled'
91
+ ? input.installation
92
+ : 'managed';
93
+ const state = version == null
94
+ ? 'unavailable'
95
+ : isUpgradeScreenSatisfied(version)
96
+ ? 'compatible'
97
+ : 'incompatible';
98
+ const older = version != null && isOlderThanUpgradeScreenMinimum(version);
99
+ // External processes are not replaced. Bundled and managed locals can receive
100
+ // an owned-cache install; that does not overwrite the user's other binaries.
101
+ const canInstall = state === 'incompatible'
102
+ && older
103
+ && installation !== 'external'
104
+ && input.platformCanInstall === true;
105
+ const guidance = canInstall
106
+ ? null
107
+ : (typeof input.guidance === 'string' && input.guidance.trim() ? input.guidance.trim() : null);
108
+ return {
109
+ state,
110
+ version,
111
+ installation,
112
+ minimumVersion: REQUIRED_OPENCODE_VERSION,
113
+ targetVersion: resolveUpgradeScreenTarget(input.pin),
114
+ canInstall,
115
+ guidance,
116
+ };
117
+ }
118
+
119
+ /**
120
+ * A running serve below the minimum, or a missing version, is not an upgrade.
121
+ * @param {{ targetVersion?: string | null, serveVersion?: string | null }} input
122
+ */
123
+ export function evaluateUpgradeScreenInstallResult(input = {}) {
124
+ const targetVersion = resolveUpgradeScreenTarget(input.targetVersion || REQUIRED_OPENCODE_VERSION);
125
+ const version = normalizeVersion(input.serveVersion);
126
+ if (!version || !isUpgradeScreenSatisfied(version) || !isOpenCode2VersionAtLeast(version, targetVersion)) {
127
+ const found = version || 'unknown';
128
+ return {
129
+ ok: false,
130
+ upgraded: false,
131
+ version,
132
+ targetVersion,
133
+ errorCode: 'UPGRADE_SCREEN_NOT_VERIFIED',
134
+ error: `OpenCode is still ${found}. OpenChamber requires ${REQUIRED_OPENCODE_VERSION} or newer.`,
135
+ };
136
+ }
137
+ return {
138
+ ok: true,
139
+ upgraded: true,
140
+ version,
141
+ targetVersion,
142
+ error: null,
143
+ errorCode: null,
144
+ };
145
+ }
146
+
147
+ async function readUpgradeScreenStatus(deps) {
148
+ const ownership = typeof deps.resolveOwnership === 'function'
149
+ ? await deps.resolveOwnership()
150
+ : { ownership: 'unknown', guidance: null };
151
+ const installation = classifyUpgradeScreenInstallation(ownership?.ownership);
152
+ const serveVersion = typeof deps.readServeVersion === 'function'
153
+ ? await Promise.resolve(deps.readServeVersion()).catch(() => null)
154
+ : null;
155
+ const cliVersion = typeof deps.readCliVersion === 'function'
156
+ ? await Promise.resolve(deps.readCliVersion()).catch(() => null)
157
+ : null;
158
+ return describeUpgradeScreen({
159
+ version: pickDetectedVersion(serveVersion, cliVersion),
160
+ installation,
161
+ platformCanInstall: typeof deps.platformCanInstall === 'boolean'
162
+ ? deps.platformCanInstall
163
+ : platformSupportsUpgradeScreenInstall(deps.platform, deps.arch),
164
+ guidance: ownership?.guidance ?? null,
165
+ pin: deps.pin,
166
+ });
167
+ }
168
+
169
+ const failure = (message, code, status = 500) => {
170
+ const error = new Error(message);
171
+ error.code = code;
172
+ error.status = status;
173
+ return error;
174
+ };
175
+
176
+ /**
177
+ * Install the screen target into the owned cache and restart through the
178
+ * caller's existing lifecycle hooks. Restores the previous binary selection
179
+ * when verification fails. Never returns upgraded:true for a failed install.
180
+ * @param {object} deps
181
+ */
182
+ export async function installRequiredOpenCode(deps) {
183
+ const status = await readUpgradeScreenStatus(deps);
184
+ if (status.state !== 'incompatible' || status.canInstall !== true) {
185
+ throw failure(
186
+ status.guidance || 'Automatic OpenCode installation is unavailable for this runtime.',
187
+ 'UPGRADE_SCREEN_UNAVAILABLE',
188
+ 409,
189
+ );
190
+ }
191
+ const target = status.targetVersion;
192
+ const previousBinary = typeof deps.getResolvedBinary === 'function' ? deps.getResolvedBinary() : null;
193
+ const previousSource = typeof deps.getResolvedBinarySource === 'function' ? deps.getResolvedBinarySource() : null;
194
+ const previousSetting = typeof deps.readConfiguredBinary === 'function'
195
+ ? await deps.readConfiguredBinary()
196
+ : undefined;
197
+ let persisted = false;
198
+ try {
199
+ if (typeof deps.install !== 'function') {
200
+ throw failure('OpenCode install is not configured.', 'UPGRADE_SCREEN_INSTALL_UNAVAILABLE', 500);
201
+ }
202
+ const installedPath = await deps.install({ version: target });
203
+ const diskVersion = typeof deps.readBinaryVersion === 'function'
204
+ ? await Promise.resolve(deps.readBinaryVersion(installedPath))
205
+ : null;
206
+ if (diskVersion !== target) {
207
+ throw failure(
208
+ `Installed OpenCode version mismatch: expected ${target}, got ${diskVersion || 'unknown'}`,
209
+ 'UPGRADE_SCREEN_BINARY_MISMATCH',
210
+ 500,
211
+ );
212
+ }
213
+ if (typeof deps.persistBinary === 'function') {
214
+ await deps.persistBinary(installedPath);
215
+ persisted = true;
216
+ }
217
+ if (typeof deps.forceBinary === 'function') {
218
+ deps.forceBinary(installedPath, 'installed');
219
+ }
220
+ if (typeof deps.restart === 'function') {
221
+ const shared = typeof deps.isSharedService === 'function' && deps.isSharedService() === true;
222
+ await deps.restart(shared ? { binaryPath: installedPath } : undefined);
223
+ }
224
+ if (typeof deps.waitReady === 'function') {
225
+ await deps.waitReady();
226
+ }
227
+ const serveVersion = typeof deps.readServeVersion === 'function'
228
+ ? await deps.readServeVersion()
229
+ : null;
230
+ const verification = evaluateUpgradeScreenInstallResult({ targetVersion: target, serveVersion });
231
+ if (!verification.ok) {
232
+ throw failure(verification.error, verification.errorCode, 500);
233
+ }
234
+ return verification;
235
+ } catch (error) {
236
+ if (persisted && typeof deps.persistBinary === 'function') {
237
+ try {
238
+ await deps.persistBinary(typeof previousSetting === 'string' ? previousSetting : '');
239
+ } catch {
240
+ // Keep the install error. A failed restore must not become success.
241
+ }
242
+ }
243
+ if (previousBinary && typeof deps.forceBinary === 'function') {
244
+ try {
245
+ deps.forceBinary(previousBinary, previousSource || 'discovered');
246
+ } catch {
247
+ // Same: report the install failure, not a restored-success.
248
+ }
249
+ }
250
+ throw error;
251
+ }
252
+ }
253
+
254
+ export function registerUpgradeScreenRoutes(app, deps) {
255
+ app.get('/api/opencode/compatibility', async (_req, res) => {
256
+ try {
257
+ res.json(await readUpgradeScreenStatus(deps));
258
+ } catch (error) {
259
+ res.status(503).json({
260
+ error: error instanceof Error ? error.message : 'Could not check OpenCode compatibility',
261
+ });
262
+ }
263
+ });
264
+
265
+ let inFlight = null;
266
+ app.post('/api/opencode/install-required', async (_req, res) => {
267
+ try {
268
+ if (!inFlight) {
269
+ inFlight = installRequiredOpenCode(deps).finally(() => {
270
+ inFlight = null;
271
+ });
272
+ }
273
+ const result = await inFlight;
274
+ if (!result || result.ok !== true || result.upgraded !== true || !result.version) {
275
+ return res.status(500).json({
276
+ success: false,
277
+ upgraded: false,
278
+ error: result?.error || 'OpenCode installation did not verify a new version',
279
+ errorCode: result?.errorCode || 'UPGRADE_SCREEN_NOT_VERIFIED',
280
+ version: result?.version ?? null,
281
+ });
282
+ }
283
+ return res.json({
284
+ success: true,
285
+ upgraded: true,
286
+ version: result.version,
287
+ targetVersion: result.targetVersion,
288
+ });
289
+ } catch (error) {
290
+ const status = Number.isInteger(error?.status) ? error.status : 500;
291
+ return res.status(status).json({
292
+ success: false,
293
+ upgraded: false,
294
+ error: error instanceof Error ? error.message : 'OpenCode installation failed',
295
+ errorCode: error?.code || 'UPGRADE_SCREEN_FAILED',
296
+ });
297
+ }
298
+ });
299
+ }
@@ -0,0 +1,196 @@
1
+ import express from 'express';
2
+ import request from 'supertest';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import {
5
+ REQUIRED_OPENCODE_VERSION,
6
+ describeUpgradeScreen,
7
+ evaluateUpgradeScreenInstallResult,
8
+ installRequiredOpenCode,
9
+ registerUpgradeScreenRoutes,
10
+ resolveUpgradeScreenTarget,
11
+ } from './upgrade-screen.js';
12
+
13
+ describe('upgrade screen version gate', () => {
14
+ it('uses 2.0.15 even when the runtime pin is older', () => {
15
+ expect(REQUIRED_OPENCODE_VERSION).toBe('2.0.15');
16
+ expect(resolveUpgradeScreenTarget('2.0.12')).toBe('2.0.15');
17
+ expect(resolveUpgradeScreenTarget('2.0.16')).toBe('2.0.16');
18
+ });
19
+
20
+ it('does not open the gate at or above 2.0.15', () => {
21
+ expect(describeUpgradeScreen({
22
+ version: '2.0.15',
23
+ installation: 'managed',
24
+ platformCanInstall: true,
25
+ })).toMatchObject({ state: 'compatible', canInstall: false, minimumVersion: '2.0.15' });
26
+ expect(describeUpgradeScreen({
27
+ version: 'v2.0.16',
28
+ installation: 'managed',
29
+ platformCanInstall: true,
30
+ }).state).toBe('compatible');
31
+ });
32
+
33
+ it('opens the gate below 2.0.15 and offers install only when this runtime can switch', () => {
34
+ expect(describeUpgradeScreen({
35
+ version: '2.0.14',
36
+ installation: 'managed',
37
+ platformCanInstall: true,
38
+ })).toMatchObject({ state: 'incompatible', canInstall: true, version: '2.0.14' });
39
+ expect(describeUpgradeScreen({
40
+ version: '1.18.30',
41
+ installation: 'managed',
42
+ platformCanInstall: true,
43
+ })).toMatchObject({ state: 'incompatible', canInstall: true });
44
+ expect(describeUpgradeScreen({
45
+ version: '2.0.14',
46
+ installation: 'external',
47
+ platformCanInstall: true,
48
+ }).canInstall).toBe(false);
49
+ expect(describeUpgradeScreen({
50
+ version: '2.0.14',
51
+ installation: 'bundled',
52
+ platformCanInstall: true,
53
+ })).toMatchObject({ state: 'incompatible', canInstall: true, installation: 'bundled' });
54
+ expect(describeUpgradeScreen({
55
+ version: '2.0.14',
56
+ installation: 'managed',
57
+ platformCanInstall: false,
58
+ }).canInstall).toBe(false);
59
+ });
60
+
61
+ it('does not treat a missing version as an old install', () => {
62
+ expect(describeUpgradeScreen({
63
+ version: null,
64
+ installation: 'managed',
65
+ platformCanInstall: true,
66
+ }).state).toBe('unavailable');
67
+ });
68
+
69
+ it('does not report an unverified install as upgraded', () => {
70
+ expect(evaluateUpgradeScreenInstallResult({
71
+ targetVersion: '2.0.15',
72
+ serveVersion: '2.0.14',
73
+ })).toMatchObject({ ok: false, upgraded: false, version: '2.0.14' });
74
+ expect(evaluateUpgradeScreenInstallResult({
75
+ targetVersion: '2.0.15',
76
+ serveVersion: null,
77
+ }).upgraded).toBe(false);
78
+ expect(evaluateUpgradeScreenInstallResult({
79
+ targetVersion: '2.0.15',
80
+ serveVersion: '2.0.15',
81
+ })).toMatchObject({ ok: true, upgraded: true, version: '2.0.15' });
82
+ });
83
+ });
84
+
85
+ describe('installRequiredOpenCode', () => {
86
+ const createDeps = (overrides = {}) => {
87
+ const calls = [];
88
+ const deps = {
89
+ resolveOwnership: async () => ({ ownership: 'global-cli', guidance: null }),
90
+ readServeVersion: async () => '2.0.14',
91
+ readCliVersion: () => '2.0.14',
92
+ platformCanInstall: true,
93
+ install: vi.fn(async () => {
94
+ calls.push('install');
95
+ return '/cache/opencode';
96
+ }),
97
+ readBinaryVersion: vi.fn(() => '2.0.15'),
98
+ persistBinary: vi.fn(async () => {
99
+ calls.push('persist');
100
+ }),
101
+ forceBinary: vi.fn(() => {
102
+ calls.push('force');
103
+ }),
104
+ restart: vi.fn(async () => {
105
+ calls.push('restart');
106
+ }),
107
+ waitReady: vi.fn(async () => {
108
+ calls.push('ready');
109
+ }),
110
+ readConfiguredBinary: async () => null,
111
+ isSharedService: () => false,
112
+ ...overrides,
113
+ };
114
+ return { deps, calls };
115
+ };
116
+
117
+ it('installs 2.0.15 and only succeeds after the running serve is verified', async () => {
118
+ const { deps, calls } = createDeps({
119
+ readServeVersion: vi.fn()
120
+ .mockResolvedValueOnce('2.0.14')
121
+ .mockResolvedValueOnce('2.0.15'),
122
+ });
123
+
124
+ await expect(installRequiredOpenCode(deps)).resolves.toMatchObject({
125
+ ok: true,
126
+ upgraded: true,
127
+ version: '2.0.15',
128
+ targetVersion: '2.0.15',
129
+ });
130
+ expect(deps.install).toHaveBeenCalledWith({ version: '2.0.15' });
131
+ expect(calls).toEqual(['install', 'persist', 'force', 'restart', 'ready']);
132
+ });
133
+
134
+ it('returns the failure reason and does not claim an upgrade when verification fails', async () => {
135
+ const { deps } = createDeps({
136
+ readServeVersion: vi.fn()
137
+ .mockResolvedValueOnce('1.18.30')
138
+ .mockResolvedValueOnce('1.18.30'),
139
+ readCliVersion: () => '1.18.30',
140
+ readBinaryVersion: () => '2.0.15',
141
+ });
142
+
143
+ await expect(installRequiredOpenCode(deps)).rejects.toThrow(/still 1\.18\.30/);
144
+ expect(deps.persistBinary).toHaveBeenCalledWith('');
145
+ });
146
+
147
+ it('does not install when the runtime cannot switch', async () => {
148
+ const { deps } = createDeps({
149
+ resolveOwnership: async () => ({
150
+ ownership: 'external-serve',
151
+ guidance: 'Restart that server yourself.',
152
+ }),
153
+ });
154
+
155
+ await expect(installRequiredOpenCode(deps)).rejects.toMatchObject({
156
+ status: 409,
157
+ message: 'Restart that server yourself.',
158
+ });
159
+ expect(deps.install).not.toHaveBeenCalled();
160
+ });
161
+ });
162
+
163
+ describe('upgrade screen routes', () => {
164
+ it('hides the gate for 2.0.15 and reports install failure without upgraded:true', async () => {
165
+ const app = express();
166
+ app.use(express.json());
167
+ const readServeVersion = vi.fn()
168
+ .mockResolvedValueOnce('2.0.15')
169
+ .mockResolvedValueOnce('2.0.12')
170
+ .mockResolvedValueOnce('2.0.12');
171
+ registerUpgradeScreenRoutes(app, {
172
+ resolveOwnership: async () => ({ ownership: 'owned-cache', guidance: null }),
173
+ readServeVersion,
174
+ readCliVersion: () => '2.0.12',
175
+ platformCanInstall: true,
176
+ install: vi.fn(async () => {
177
+ throw new Error('registry unreachable');
178
+ }),
179
+ readBinaryVersion: () => '2.0.15',
180
+ persistBinary: vi.fn(),
181
+ forceBinary: vi.fn(),
182
+ restart: vi.fn(),
183
+ });
184
+
185
+ const compatible = await request(app).get('/api/opencode/compatibility').expect(200);
186
+ expect(compatible.body).toMatchObject({ state: 'compatible', version: '2.0.15', minimumVersion: '2.0.15' });
187
+
188
+ const failed = await request(app).post('/api/opencode/install-required').expect(500);
189
+ expect(failed.body).toMatchObject({
190
+ success: false,
191
+ upgraded: false,
192
+ error: 'registry unreachable',
193
+ });
194
+ expect(failed.body.upgraded).not.toBe(true);
195
+ });
196
+ });
@@ -0,0 +1,157 @@
1
+ export const OPENCODE_V1_MIGRATION_PATH = '/api/experimental/migration/v1';
2
+
3
+ // Backfill notes must stay readable by UI and docs. Do not drop them when the
4
+ // gate blocks or later admits a migrated library.
5
+ export const V1_MIGRATION_USER_NOTICE = [
6
+ 'V1 history is backfilled in the opencode2 process.',
7
+ 'Message ids are reused.',
8
+ 'In-progress tools become interrupted.',
9
+ 'V1 subtasks do not appear in v2.',
10
+ ].join(' ');
11
+
12
+ const MIGRATION_PHASES = new Set(['required', 'running', 'completed', 'error']);
13
+
14
+ const stringifyError = (error) => {
15
+ if (error instanceof Error && error.message) {
16
+ return error.message;
17
+ }
18
+ if (typeof error === 'string' && error.trim()) {
19
+ return error;
20
+ }
21
+ return 'V1 migration status request failed';
22
+ };
23
+
24
+ const blocked = (phase, extra = {}) => ({
25
+ admitTranscript: false,
26
+ phase,
27
+ userNotice: V1_MIGRATION_USER_NOTICE,
28
+ ...extra,
29
+ });
30
+
31
+ const admitted = (phase, extra = {}) => ({
32
+ admitTranscript: true,
33
+ phase,
34
+ ...extra,
35
+ });
36
+
37
+ const normalizeProgress = (progress) => {
38
+ if (!progress || typeof progress !== 'object' || Array.isArray(progress)) {
39
+ return undefined;
40
+ }
41
+ const result = {
42
+ label: typeof progress.label === 'string' ? progress.label : '',
43
+ };
44
+ if (Number.isFinite(progress.numerator) && progress.numerator >= 0) {
45
+ result.numerator = progress.numerator;
46
+ }
47
+ if (Number.isFinite(progress.denominator) && progress.denominator >= 0) {
48
+ result.denominator = progress.denominator;
49
+ }
50
+ return result;
51
+ };
52
+
53
+ const resolveHttpStatus = (input) => {
54
+ if (typeof input.httpStatus === 'number') {
55
+ return input.httpStatus;
56
+ }
57
+ if (typeof input.status === 'number') {
58
+ return input.status;
59
+ }
60
+ return undefined;
61
+ };
62
+
63
+ const resolveBody = (input) => {
64
+ if (input.body !== undefined) {
65
+ return input.body;
66
+ }
67
+ if (typeof input.status === 'string') {
68
+ return input;
69
+ }
70
+ return null;
71
+ };
72
+
73
+ const resolveTransportError = (input) => {
74
+ if (input instanceof Error || typeof input === 'string') {
75
+ return input;
76
+ }
77
+ if (input?.error instanceof Error || typeof input?.error === 'string') {
78
+ if (input.body === undefined && typeof input.status !== 'string') {
79
+ return input.error;
80
+ }
81
+ }
82
+ return null;
83
+ };
84
+
85
+ // Pure admission decision for GET /api/experimental/migration/v1.
86
+ // required/running/error never become an empty-list success.
87
+ export const evaluateV1MigrationGate = (input = {}) => {
88
+ if (input == null) {
89
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
90
+ }
91
+
92
+ const transportError = resolveTransportError(input);
93
+ if (transportError) {
94
+ return blocked('error', { error: stringifyError(transportError) });
95
+ }
96
+
97
+ const httpStatus = resolveHttpStatus(input);
98
+ if (httpStatus === 404) {
99
+ return admitted('absent');
100
+ }
101
+ if (Number.isInteger(httpStatus) && httpStatus >= 400) {
102
+ return blocked('error', { error: `V1 migration status HTTP ${httpStatus}` });
103
+ }
104
+
105
+ const body = resolveBody(input);
106
+ const phase = body && typeof body === 'object' ? body.status : undefined;
107
+
108
+ if (phase === 'required') {
109
+ return blocked('required');
110
+ }
111
+ if (phase === 'running') {
112
+ return blocked('running', { progress: normalizeProgress(body.progress) });
113
+ }
114
+ if (phase === 'completed') {
115
+ return admitted('completed', { userNotice: V1_MIGRATION_USER_NOTICE });
116
+ }
117
+ if (phase === 'error') {
118
+ return blocked('error', {
119
+ error: typeof body.error === 'string' && body.error.trim()
120
+ ? body.error
121
+ : 'V1 migration failed',
122
+ });
123
+ }
124
+
125
+ if (MIGRATION_PHASES.has(phase) === false) {
126
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
127
+ }
128
+
129
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
130
+ };
131
+
132
+ // Official protocol only publishes GET status. Backfill is owned by opencode2;
133
+ // clients poll this helper and do not POST unless a later API requires it.
134
+ export const fetchV1MigrationGate = async ({
135
+ url,
136
+ headers = {},
137
+ signal,
138
+ fetchImpl = globalThis.fetch,
139
+ } = {}) => {
140
+ try {
141
+ const response = await fetchImpl(url, {
142
+ method: 'GET',
143
+ headers: {
144
+ Accept: 'application/json',
145
+ ...headers,
146
+ },
147
+ signal,
148
+ });
149
+ const body = await response.json().catch(() => null);
150
+ return evaluateV1MigrationGate({
151
+ httpStatus: response.status,
152
+ body,
153
+ });
154
+ } catch (error) {
155
+ return evaluateV1MigrationGate({ error });
156
+ }
157
+ };