@openchambery/web 2.0.0-beta.1 → 2.0.0-beta.11

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 (296) hide show
  1. package/README.md +15 -11
  2. package/bin/cli-opencode.test.js +4 -4
  3. package/bin/cli.js +1 -1
  4. package/bin/lib/cli-startup.js +1 -1
  5. package/bin/lib/cli-startup.test.js +18 -4
  6. package/dist/assets/{AgentAvatar-CLWWNu_Q.js → AgentAvatar-pLaqWWmB.js} +1 -1
  7. package/dist/assets/AssistantView-C4GkABnI.js +3 -0
  8. package/dist/assets/{DiagramView-xuSJTnv7.js → DiagramView-ByYLIfWo.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-Ov0D96d0.js → FadeInOnReveal-MuJAzJq_.js} +1 -1
  10. package/dist/assets/Icon-Dz2kO9RN.js +1 -0
  11. package/dist/assets/MarkdownRenderer-CPU6kSHd.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-SKi4oG0c.js → MarkdownRendererImpl-2hw5cdqQ.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-DXZQkLTa.js +3 -0
  14. package/dist/assets/{MobileDetailNavigation-Bnx59U_J.js → MobileDetailNavigation-C9BzieQY.js} +1 -1
  15. package/dist/assets/{MobileShareBridge-BQlP4J-F.js → MobileShareBridge-2_iOfzqO.js} +2 -2
  16. package/dist/assets/MobileSurface-DNVA7K_1.js +2 -0
  17. package/dist/assets/{MultiRunWindow-ChZGob0v.js → MultiRunWindow-QA6pMrJ9.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-DtQJxZG-.js → OnboardingScreen-XWkr7MjT.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-DCWyGseO.js +2 -0
  20. package/dist/assets/SettingsView-dWuu0VIb.js +25 -0
  21. package/dist/assets/{SettingsWindow-DfnsFa8y.js → SettingsWindow-CetgiJMX.js} +1 -1
  22. package/dist/assets/TerminalView-BuXuo7gm.js +8 -0
  23. package/dist/assets/{ToolOutputDialog-BejmjrRk.js → ToolOutputDialog-DNSSOaCt.js} +2 -2
  24. package/dist/assets/appThemeRegistry-Cb7qFBsc.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-D-rATN7w.js → button-Doq9FI1I.js} +1 -1
  27. package/dist/assets/{collapsible-BXRd5_Hr.js → collapsible-CYUeu3_N.js} +1 -1
  28. package/dist/assets/{context-menu-jGtH6fR9.js → context-menu-C8kOMVtC.js} +1 -1
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-DbxnIQdS.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-dvxF4V2p.js} +1 -1
  31. package/dist/assets/{diagram-DQf7Y_uR.js → diagram-B1p9Yn1h.js} +1 -1
  32. package/dist/assets/{dialog-bfbvR4VH.js → dialog-D08-Lg-e.js} +1 -1
  33. package/dist/assets/es-Dsmc0A_3.js +15 -0
  34. package/dist/assets/fileMentionTouchSelection-BCAKb-NS.js +3 -0
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-X773xxvX.js} +1 -1
  36. package/dist/assets/fr-C_ZlZDI2.js +13 -0
  37. package/dist/assets/gitApi-3VBj3fbR.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-BMztRu0c.js} +1 -1
  39. package/dist/assets/{input-8r6F_Fza.js → input-DZZuQ1IL.js} +1 -1
  40. package/dist/assets/insertionBoundaries-BMuCcvPy.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-COEILjRF.js +169 -0
  44. package/dist/assets/main-D2BzpnUt.css +1 -0
  45. package/dist/assets/{main-CCE_4y5F.js → main-DsUewWPt.js} +2 -2
  46. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  47. package/dist/assets/{markdownHeightCache-1Qo6y6IJ.js → markdownHeightCache-DBgXGze8.js} +1 -1
  48. package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
  49. package/dist/assets/mcpQueries-B05QR67V.js +1 -0
  50. package/dist/assets/miniChat-D7IPMm-k.js +2 -0
  51. package/dist/assets/mobile-BnCoDxnv.js +2 -0
  52. package/dist/assets/multirun-BDmFjX9N.js +4 -0
  53. package/dist/assets/persistence-DznVIKwz.js +3 -0
  54. package/dist/assets/pl-BCf5uLlK.js +15 -0
  55. package/dist/assets/platform-DSTtx-DI.js +6 -0
  56. package/dist/assets/projectMeta-C5gcAd5f.js +3 -0
  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-DsAaWCss.js +2 -0
  60. package/dist/assets/renderMobileApp-DOaoohlp.js +28 -0
  61. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  62. package/dist/assets/{runtimeConfig-BMvqa5we.js → runtimeConfig-CzPK1uMj.js} +1 -1
  63. package/dist/assets/runtimeEndpointReset-DKjVJCdt.js +149 -0
  64. package/dist/assets/sessionChangeSummary-CktSWasa.js +2 -0
  65. package/dist/assets/{sessionLookup-gh-hVRIc.js → sessionLookup-CDA1aAcI.js} +1 -1
  66. package/dist/assets/{sortable-tabs-strip-CudoSgV4.js → sortable-tabs-strip-Q3bb6MVj.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-DiF6JmBP.js → terminalApi-Da_DO_Uh.js} +1 -1
  70. package/dist/assets/textarea--CYehp07.js +1 -0
  71. package/dist/assets/{tooltip-CQvpK-UV.js → tooltip-CGLzBwEK.js} +1 -1
  72. package/dist/assets/uk-BgFzz8m2.js +15 -0
  73. package/dist/assets/{url-_z7evzdK.js → url-Byz3kd29.js} +1 -1
  74. package/dist/assets/{useAppFontEffects-Cpmpo7Wb.js → useAppFontEffects-2zzPMQF2.js} +64 -45
  75. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
  76. package/dist/assets/useDesktopWindowControlsLayout-B-2YooUd.js +1 -0
  77. package/dist/assets/useEffectiveDirectory-K3U4eL5F.js +1 -0
  78. package/dist/assets/{useFontPreferences-DcbXQOkc.js → useFontPreferences-D9OIZs1p.js} +1 -1
  79. package/dist/assets/useMobileNavigationStore-B0zSdCwP.js +414 -0
  80. package/dist/assets/useSessionAutoCleanup-BCXHAcbX.js +2 -0
  81. package/dist/assets/useUIStore-syVmbuDA.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-Bqn4AiBL.js → useWorkerHighlightedLines-CsoMQNik.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/zh-CN-BhqSLgUY.js +15 -0
  86. package/dist/assets/zh-TW-BsaBgY-p.js +15 -0
  87. package/dist/index.html +12 -12
  88. package/dist/mini-chat.html +12 -12
  89. package/dist/mobile.html +12 -12
  90. package/package.json +3 -2
  91. package/server/index.js +359 -63
  92. package/server/lib/assistants/DOCUMENTATION.md +14 -9
  93. package/server/lib/assistants/contracts.js +11 -1
  94. package/server/lib/assistants/projection-history.test.js +265 -18
  95. package/server/lib/assistants/routes.js +6 -0
  96. package/server/lib/assistants/service.js +417 -231
  97. package/server/lib/assistants/service.test.js +392 -148
  98. package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
  99. package/server/lib/browser-provider/contract.js +312 -0
  100. package/server/lib/browser-provider/host.js +421 -0
  101. package/server/lib/browser-provider/host.test.js +304 -0
  102. package/server/lib/browser-provider/routes.js +122 -0
  103. package/server/lib/browser-provider/routes.test.js +120 -0
  104. package/server/lib/browser-provider/selection-store.js +43 -0
  105. package/server/lib/browser-provider/selection-store.test.js +35 -0
  106. package/server/lib/conversations/DOCUMENTATION.md +1 -1
  107. package/server/lib/conversations/service.js +1 -0
  108. package/server/lib/conversations/service.test.js +16 -0
  109. package/server/lib/conversations/validation.js +11 -1
  110. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  111. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  112. package/server/lib/event-stream/global-hub.js +49 -4
  113. package/server/lib/event-stream/reasoning-projection.js +16 -1
  114. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  115. package/server/lib/event-stream/runtime.js +57 -20
  116. package/server/lib/event-stream/runtime.test.js +112 -0
  117. package/server/lib/llm/DOCUMENTATION.md +33 -9
  118. package/server/lib/llm/catalog.js +1 -1
  119. package/server/lib/llm/completions.js +5 -1
  120. package/server/lib/llm/completions.test.js +3 -3
  121. package/server/lib/llm/generate.js +237 -145
  122. package/server/lib/llm/generate.test.js +201 -46
  123. package/server/lib/llm/routes.js +2 -0
  124. package/server/lib/message-queue/runtime.js +15 -2
  125. package/server/lib/notifications/DOCUMENTATION.md +3 -1
  126. package/server/lib/notifications/runtime.js +133 -31
  127. package/server/lib/notifications/runtime.test.js +103 -0
  128. package/server/lib/notifications/template-runtime.js +130 -38
  129. package/server/lib/notifications/template-runtime.test.js +96 -0
  130. package/server/lib/opencode/DOCUMENTATION.md +89 -17
  131. package/server/lib/opencode/agent-document.d.ts +28 -0
  132. package/server/lib/opencode/agent-document.js +280 -0
  133. package/server/lib/opencode/agent-document.test.js +137 -0
  134. package/server/lib/opencode/agents.js +96 -0
  135. package/server/lib/opencode/auth-state-runtime.js +5 -0
  136. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  137. package/server/lib/opencode/config-entity-routes.js +42 -99
  138. package/server/lib/opencode/config-entity-routes.test.js +107 -0
  139. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  140. package/server/lib/opencode/ensure-cli.d.ts +8 -0
  141. package/server/lib/opencode/ensure-cli.js +1 -2
  142. package/server/lib/opencode/env-runtime.js +40 -26
  143. package/server/lib/opencode/env-runtime.test.js +10 -17
  144. package/server/lib/opencode/feature-routes-runtime.js +53 -4
  145. package/server/lib/opencode/feature-routes-runtime.test.js +25 -0
  146. package/server/lib/opencode/http-server-connections.js +98 -0
  147. package/server/lib/opencode/http-server-connections.test.js +321 -0
  148. package/server/lib/opencode/lifecycle.js +349 -39
  149. package/server/lib/opencode/lifecycle.test.js +322 -0
  150. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  151. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  152. package/server/lib/opencode/openchamber-routes.js +7 -1
  153. package/server/lib/opencode/opencode2-pin.js +4 -4
  154. package/server/lib/opencode/opencode2-pin.test.js +1 -0
  155. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +127 -4
  156. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  157. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  158. package/server/lib/opencode/plugin-routes.js +3 -18
  159. package/server/lib/opencode/plugin-routes.test.js +9 -9
  160. package/server/lib/opencode/provider-catalog.js +58 -12
  161. package/server/lib/opencode/provider-catalog.test.js +48 -0
  162. package/server/lib/opencode/proxy.js +254 -46
  163. package/server/lib/opencode/proxy.test.js +238 -5
  164. package/server/lib/opencode/routes.js +326 -62
  165. package/server/lib/opencode/routes.test.js +278 -12
  166. package/server/lib/opencode/runtime-contract.js +401 -0
  167. package/server/lib/opencode/runtime-contract.test.js +214 -0
  168. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  169. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  170. package/server/lib/opencode/server-utils-runtime.js +33 -0
  171. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  172. package/server/lib/opencode/shared-service.js +147 -0
  173. package/server/lib/opencode/shared-service.test.js +93 -0
  174. package/server/lib/opencode/shared.js +3 -3
  175. package/server/lib/opencode/shutdown-runtime.js +15 -6
  176. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  177. package/server/lib/opencode/skill-routes.js +20 -33
  178. package/server/lib/opencode/skill-routes.test.js +35 -2
  179. package/server/lib/opencode/skills.test.js +15 -0
  180. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  181. package/server/lib/opencode/static-routes-runtime.js +3 -1
  182. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  183. package/server/lib/opencode/upgrade-screen.d.ts +30 -0
  184. package/server/lib/opencode/upgrade-screen.js +299 -0
  185. package/server/lib/opencode/upgrade-screen.test.js +196 -0
  186. package/server/lib/opencode/v2-client.js +14 -3
  187. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  188. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  189. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  190. package/server/lib/permission-auto-accept/runtime.js +14 -7
  191. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  192. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  193. package/server/lib/scheduled-tasks/DOCUMENTATION.md +8 -0
  194. package/server/lib/scheduled-tasks/runtime.js +52 -4
  195. package/server/lib/scheduled-tasks/runtime.test.js +80 -1
  196. package/server/lib/session-goal/DOCUMENTATION.md +113 -13
  197. package/server/lib/session-goal/routes.js +9 -0
  198. package/server/lib/session-goal/runtime.js +1579 -117
  199. package/server/lib/session-goal/runtime.test.js +1453 -84
  200. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  201. package/server/lib/session-index/event-ingest.js +89 -16
  202. package/server/lib/session-index/event-ingest.test.js +73 -0
  203. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  204. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  205. package/server/lib/session-index/routes.js +63 -10
  206. package/server/lib/session-index/routes.test.js +8 -8
  207. package/server/lib/session-index/service.js +7 -1
  208. package/server/lib/session-index/sync-runtime.js +161 -34
  209. package/server/lib/session-index/sync-runtime.test.js +155 -1
  210. package/server/lib/session-metadata/DOCUMENTATION.md +73 -18
  211. package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
  212. package/server/lib/session-metadata/opencode-session-record.js +316 -0
  213. package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
  214. package/server/lib/session-metadata/routes.js +112 -9
  215. package/server/lib/session-metadata/routes.test.js +47 -2
  216. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  217. package/server/lib/session-metadata/session-archive.js +654 -0
  218. package/server/lib/session-metadata/session-archive.test.js +359 -0
  219. package/server/lib/session-metadata/session-metadata-store.d.ts +38 -0
  220. package/server/lib/session-metadata/session-metadata-store.js +270 -83
  221. package/server/lib/session-metadata/session-metadata-store.test.js +205 -10
  222. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  223. package/server/lib/session-metadata/session-projection.js +279 -0
  224. package/server/lib/session-metadata/session-projection.test.js +64 -0
  225. package/server/lib/session-metadata/system-session.js +55 -0
  226. package/server/lib/session-metadata/system-session.test.js +55 -0
  227. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  228. package/server/lib/session-title/native-runtime.test.js +167 -0
  229. package/server/lib/session-title/runtime.js +69 -44
  230. package/server/lib/session-title/session-access.js +46 -0
  231. package/server/lib/session-title/session-access.test.js +91 -0
  232. package/server/lib/session-turn-pages/DOCUMENTATION.md +9 -8
  233. package/server/lib/session-turn-pages/routes.js +17 -102
  234. package/server/lib/session-turn-pages/routes.test.js +281 -0
  235. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  236. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  237. package/server/lib/small-model/DOCUMENTATION.md +101 -147
  238. package/server/lib/small-model/catalog.js +31 -51
  239. package/server/lib/small-model/catalog.test.js +65 -10
  240. package/server/lib/small-model/custom-api.js +68 -0
  241. package/server/lib/small-model/custom-api.test.js +64 -0
  242. package/server/lib/small-model/index.js +124 -240
  243. package/server/lib/small-model/resolve.js +35 -120
  244. package/server/lib/small-model/resolve.test.js +20 -110
  245. package/server/lib/small-model/routes.js +1 -3
  246. package/server/lib/small-model/summary-settings.test.js +157 -253
  247. package/server/opencode-proxy.test.js +2 -0
  248. package/dist/assets/AssistantView-BTCkm8yk.js +0 -3
  249. package/dist/assets/Icon-DD-BuMfZ.js +0 -1
  250. package/dist/assets/MarkdownRenderer-bc_p1H7l.js +0 -2
  251. package/dist/assets/MarkstreamRendererImpl-CnkodGJW.js +0 -3
  252. package/dist/assets/MobileSurface-BXLeiZ4d.js +0 -2
  253. package/dist/assets/OverlayScrollbar-pMwxaCca.js +0 -2
  254. package/dist/assets/SettingsView-B5I0KDTZ.js +0 -25
  255. package/dist/assets/TerminalView-Dh9MWCpS.js +0 -8
  256. package/dist/assets/appThemeRegistry-COGfxTFV.js +0 -1
  257. package/dist/assets/es-c52LTHHe.js +0 -15
  258. package/dist/assets/fileMentionTouchSelection-BvrNCroG.js +0 -4
  259. package/dist/assets/fr-PisFbUE8.js +0 -13
  260. package/dist/assets/gitApi-ButwzQ0N.js +0 -9
  261. package/dist/assets/insertionBoundaries-DGs7S5zv.js +0 -1
  262. package/dist/assets/ja-C8Ikun3Z.js +0 -15
  263. package/dist/assets/ko-ClFys5o2.js +0 -15
  264. package/dist/assets/main-Dn9rpsgl.js +0 -169
  265. package/dist/assets/main-qSeeJRW1.css +0 -1
  266. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  267. package/dist/assets/markdownTheme-DOfGg-A7.js +0 -1
  268. package/dist/assets/miniChat-BpjxVqgg.js +0 -2
  269. package/dist/assets/mobile-nMJCaNq1.js +0 -2
  270. package/dist/assets/multirun-BvSG94mW.js +0 -4
  271. package/dist/assets/persistence-Ce3AnpEr.js +0 -3
  272. package/dist/assets/pl-CnaKWN5F.js +0 -15
  273. package/dist/assets/platform-CWTIp02q.js +0 -4
  274. package/dist/assets/projectMeta-DOe49dM_.js +0 -3
  275. package/dist/assets/pt-BR-s8OpfRI6.js +0 -15
  276. package/dist/assets/renderElectronMiniChatApp-R6dKh79f.js +0 -2
  277. package/dist/assets/renderMobileApp-y-S8y8v_.js +0 -28
  278. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  279. package/dist/assets/runtimeEndpointReset-B6BxhvR0.js +0 -147
  280. package/dist/assets/sessionChangeSummary-VVBlEXuh.js +0 -2
  281. package/dist/assets/src-BzX5UI-c.css +0 -2
  282. package/dist/assets/store-DJczmczv.js +0 -15
  283. package/dist/assets/textarea-3XR8sUfy.js +0 -1
  284. package/dist/assets/uk-CaX0iqkB.js +0 -15
  285. package/dist/assets/useDesktopWindowControlsLayout-B1GX59-L.js +0 -1
  286. package/dist/assets/useEffectiveDirectory-CN6JoCFw.js +0 -1
  287. package/dist/assets/useMobileNavigationStore-BVjFtXSy.js +0 -414
  288. package/dist/assets/useSessionAutoCleanup-BLjymaFt.js +0 -2
  289. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  290. package/dist/assets/vendor-opencode-client-BOe2qpQb.js +0 -12
  291. package/dist/assets/zh-CN-20MYEjha.js +0 -15
  292. package/dist/assets/zh-TW-DiN04qTw.js +0 -15
  293. package/server/lib/small-model/call.js +0 -427
  294. package/server/lib/small-model/call.test.js +0 -382
  295. package/server/lib/small-model/opencode-session.js +0 -183
  296. package/server/lib/small-model/opencode-session.test.js +0 -147
@@ -1,6 +1,25 @@
1
1
  import { createProjectIdFromPath } from '../projects/project-id.js';
2
2
  import { projectBootstrapSettingsResponse } from './settings-helpers.js';
3
- import { PINNED_OPENCODE2_VERSION, isOpenCode1xVersion, resolveOpenCode2UpgradeTarget } from './opencode2-pin.js';
3
+ import {
4
+ PINNED_OPENCODE2_VERSION,
5
+ evaluateOpenCodeHealthBody,
6
+ isOpenCode1xVersion,
7
+ resolveOpenCode2UpgradeTarget,
8
+ } from './opencode2-pin.js';
9
+ import {
10
+ installPinnedOpenCode2Cli,
11
+ readOpenCode2BinaryVersion,
12
+ resolveOpenChamberDataDir,
13
+ } from './ensure-cli.js';
14
+ import { registerUpgradeScreenRoutes } from './upgrade-screen.js';
15
+ import { evaluateRuntimeContract } from './runtime-contract.js';
16
+ import {
17
+ buildUpgradeStatusSnapshot,
18
+ classifyRuntimeOwnership,
19
+ createUpgradeOperationState,
20
+ evaluateOwnedUpgradeResult,
21
+ resolveOwnedCacheBinaryPath,
22
+ } from './owned-runtime-upgrade.js';
4
23
  import fs from 'fs';
5
24
  import os from 'os';
6
25
  import path from 'path';
@@ -24,8 +43,22 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
24
43
  getOpenCodeAuthHeaders,
25
44
  onSettingsPersisted,
26
45
  getIsExternalOpenCode = () => false,
46
+ getIsSharedOpenCodeService = () => false,
47
+ forceResolvedOpenCodeBinary = null,
48
+ restartOpenCode = null,
49
+ waitForOpenCodeReady = null,
50
+ getRuntimeContract = () => null,
51
+ getOpenCodeServeVersion = () => null,
52
+ getOpenCodeCliVersion = () => null,
53
+ getResolvedOpenCodeBinary = () => null,
54
+ getResolvedOpenCodeBinarySource = () => null,
55
+ getActiveSessionCount = () => 0,
56
+ openchamberDataDir = null,
27
57
  } = dependencies;
28
58
 
59
+ const upgradeOperation = createUpgradeOperationState();
60
+ const resolveDataDir = () => openchamberDataDir || resolveOpenChamberDataDir();
61
+
29
62
  let authLibrary = null;
30
63
  const pendingMcpAuthContextByState = new Map();
31
64
  const PENDING_MCP_AUTH_TTL_MS = 30 * 60 * 1000;
@@ -58,10 +91,81 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
58
91
  });
59
92
  const health = await healthResponse.json().catch(() => null);
60
93
  if (!healthResponse.ok) {
61
- return { ok: false, status: healthResponse.status, error: health?.error || healthResponse.statusText };
94
+ return {
95
+ ok: false,
96
+ status: healthResponse.status,
97
+ error: health?.error || healthResponse.statusText,
98
+ authenticated: healthResponse.status === 401 || healthResponse.status === 403 ? false : null,
99
+ };
62
100
  }
63
- const currentVersion = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
64
- return { ok: true, currentVersion };
101
+ const gate = evaluateOpenCodeHealthBody(health);
102
+ const currentVersion = gate.version
103
+ || (typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null);
104
+ return { ok: gate.ok, currentVersion, healthOk: gate.ok, authenticated: true };
105
+ };
106
+
107
+ const resolveOwnershipContext = async () => {
108
+ const isExternal = getIsExternalOpenCode() === true;
109
+ let binarySource = typeof getResolvedOpenCodeBinarySource === 'function'
110
+ ? getResolvedOpenCodeBinarySource()
111
+ : null;
112
+ let binaryPath = typeof getResolvedOpenCodeBinary === 'function'
113
+ ? getResolvedOpenCodeBinary()
114
+ : null;
115
+ try {
116
+ const settings = await readSettingsFromDiskMigrated();
117
+ const resolution = await getOpenCodeResolutionSnapshot(settings);
118
+ binarySource = binarySource || resolution?.source || resolution?.detectedSourceNow || null;
119
+ binaryPath = binaryPath || resolution?.resolved || null;
120
+ if (resolution?.source === 'bundled' || resolution?.detectedSourceNow === 'bundled') {
121
+ binarySource = 'bundled';
122
+ }
123
+ } catch {
124
+ // Resolution failures still allow ownership classification from runtime flags.
125
+ }
126
+ return classifyRuntimeOwnership({
127
+ isExternal,
128
+ isSharedService: getIsSharedOpenCodeService() === true,
129
+ binarySource,
130
+ binaryPath,
131
+ dataDir: resolveDataDir(),
132
+ });
133
+ };
134
+
135
+ const buildLiveContract = async ({ fresh = false } = {}) => {
136
+ const cached = typeof getRuntimeContract === 'function' ? getRuntimeContract() : null;
137
+ const serveProbe = await readOpenCodeCurrentVersion().catch(() => ({ ok: false, currentVersion: null }));
138
+ const serveVersion = serveProbe.currentVersion
139
+ || (!fresh && typeof getOpenCodeServeVersion === 'function' ? getOpenCodeServeVersion() : null);
140
+ const binaryPath = typeof getResolvedOpenCodeBinary === 'function' ? getResolvedOpenCodeBinary() : null;
141
+ const cliVersion = (typeof getOpenCodeCliVersion === 'function' ? getOpenCodeCliVersion() : null)
142
+ || (binaryPath ? readOpenCode2BinaryVersion(binaryPath) : null)
143
+ || null;
144
+ return evaluateRuntimeContract({
145
+ serveVersion,
146
+ cliVersion,
147
+ reachable: fresh ? serveProbe.ok === true : serveProbe.ok === true || Boolean(serveVersion) || Boolean(cached?.reachable),
148
+ authenticated: serveProbe.authenticated ?? (fresh ? null : cached?.authenticated) ?? null,
149
+ healthOk: serveProbe.healthOk ?? serveProbe.ok ?? (fresh ? false : cached?.healthOk) ?? null,
150
+ migrationAdmitTranscript: cached?.migrationExecutable ?? null,
151
+ migrationPhase: cached?.migrationPhase ?? null,
152
+ migrationError: cached?.migrationError ?? null,
153
+ });
154
+ };
155
+
156
+ const buildUpgradeStatus = async () => {
157
+ const ownership = await resolveOwnershipContext();
158
+ const contract = await buildLiveContract();
159
+ const activeCount = typeof getActiveSessionCount === 'function' ? Number(getActiveSessionCount()) || 0 : 0;
160
+ return buildUpgradeStatusSnapshot({
161
+ ownership,
162
+ serveVersion: contract.serveVersion,
163
+ cliVersion: contract.cliVersion,
164
+ targetVersion: PINNED_OPENCODE2_VERSION,
165
+ contract,
166
+ operation: upgradeOperation.getState(),
167
+ hasActiveTasks: activeCount > 0,
168
+ });
65
169
  };
66
170
 
67
171
  const parseVersionForComparison = (value) => {
@@ -150,6 +254,35 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
150
254
  return fallback;
151
255
  };
152
256
 
257
+ registerUpgradeScreenRoutes(app, {
258
+ resolveOwnership: resolveOwnershipContext,
259
+ readServeVersion: async () => {
260
+ const probe = await readOpenCodeCurrentVersion().catch(() => ({ currentVersion: null }));
261
+ return probe.currentVersion || null;
262
+ },
263
+ readCliVersion: () => {
264
+ const fromRuntime = typeof getOpenCodeCliVersion === 'function' ? getOpenCodeCliVersion() : null;
265
+ if (fromRuntime) return fromRuntime;
266
+ const binaryPath = typeof getResolvedOpenCodeBinary === 'function' ? getResolvedOpenCodeBinary() : null;
267
+ return binaryPath ? readOpenCode2BinaryVersion(binaryPath) : null;
268
+ },
269
+ readConfiguredBinary: async () => {
270
+ const settings = await readSettingsFromDiskMigrated();
271
+ return typeof settings?.opencodeBinary === 'string' ? settings.opencodeBinary : null;
272
+ },
273
+ persistBinary: async (binaryPath) => {
274
+ await persistSettings({ opencodeBinary: binaryPath });
275
+ },
276
+ getResolvedBinary: getResolvedOpenCodeBinary,
277
+ getResolvedBinarySource: getResolvedOpenCodeBinarySource,
278
+ forceBinary: typeof forceResolvedOpenCodeBinary === 'function' ? forceResolvedOpenCodeBinary : null,
279
+ restart: typeof restartOpenCode === 'function' ? restartOpenCode : null,
280
+ waitReady: typeof waitForOpenCodeReady === 'function' ? waitForOpenCodeReady : null,
281
+ isSharedService: () => getIsSharedOpenCodeService() === true,
282
+ install: (options) => installPinnedOpenCode2Cli({ ...options, dataDir: resolveDataDir() }),
283
+ readBinaryVersion: readOpenCode2BinaryVersion,
284
+ });
285
+
153
286
  app.get('/api/config/settings', async (req, res) => {
154
287
  try {
155
288
  const settings = await readSettingsFromDiskMigrated();
@@ -185,17 +318,17 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
185
318
 
186
319
  app.post('/api/opencode/upgrade', async (req, res) => {
187
320
  try {
188
- if (getIsExternalOpenCode()) {
189
- return res.status(409).json({
190
- success: false,
191
- error: 'This is your own OpenCode serve. OpenChamber will not upgrade or restart it.',
192
- });
193
- }
194
-
195
- if (await isBundledOpenCodeBinaryActive()) {
196
- return res.status(409).json({
321
+ const ownership = await resolveOwnershipContext();
322
+ if (!ownership.canUpgradeInApp) {
323
+ const status = ownership.ownership === 'external-serve' || ownership.ownership === 'bundled' ? 409 : 409;
324
+ return res.status(status).json({
197
325
  success: false,
198
- error: 'OpenCode is bundled with OpenChamber Desktop and cannot be upgraded separately.',
326
+ error: ownership.guidance
327
+ || 'OpenChamber can only upgrade its owned OpenCode cache in-app.',
328
+ errorCode: ownership.reason || 'UPGRADE_NOT_MANAGED',
329
+ ownership: ownership.ownership,
330
+ management: ownership.management,
331
+ guidance: ownership.guidance,
199
332
  });
200
333
  }
201
334
 
@@ -206,23 +339,136 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
206
339
  return res.status(400).json({
207
340
  success: false,
208
341
  error: 'OpenCode upgrade refuses 1.x targets. Only pinned opencode2 is allowed.',
342
+ errorCode: 'OPENCODE_UPGRADE_1X_REFUSED',
209
343
  });
210
344
  }
211
345
  const target = resolveOpenCode2UpgradeTarget(rawTarget);
346
+ const confirmActive = req.body?.confirmActiveTasks === true || req.body?.force === true;
347
+ const activeCount = typeof getActiveSessionCount === 'function' ? Number(getActiveSessionCount()) || 0 : 0;
348
+ if (activeCount > 0 && !confirmActive) {
349
+ return res.status(409).json({
350
+ success: false,
351
+ error: 'OpenCode has active sessions. Confirm upgrade to interrupt them, or wait until idle.',
352
+ errorCode: 'UPGRADE_ACTIVE_TASKS',
353
+ hasActiveTasks: true,
354
+ activeSessionCount: activeCount,
355
+ });
356
+ }
357
+
358
+ const begin = upgradeOperation.begin(target);
359
+ if (!begin.ok) {
360
+ return res.status(begin.status).json(begin.body);
361
+ }
362
+
363
+ const expectedBinaryPath = resolveOwnedCacheBinaryPath(target, { dataDir: resolveDataDir() });
364
+ let installedPath = expectedBinaryPath;
365
+ const previousBinary = getResolvedOpenCodeBinary();
366
+ const previousSource = getResolvedOpenCodeBinarySource();
212
367
 
213
368
  try {
214
- await refreshOpenCodeAfterConfigChange('OpenCode upgrade');
215
- } catch (restartError) {
369
+ upgradeOperation.setPhase('download');
370
+ installedPath = await installPinnedOpenCode2Cli({
371
+ version: target,
372
+ dataDir: resolveDataDir(),
373
+ });
374
+ const diskVersion = readOpenCode2BinaryVersion(installedPath);
375
+ if (diskVersion !== target) {
376
+ const error = new Error(
377
+ `Owned-cache binary version mismatch after install: expected ${target}, got ${diskVersion || 'unknown'}`,
378
+ );
379
+ error.code = 'UPGRADE_BINARY_VERSION_MISMATCH';
380
+ throw error;
381
+ }
382
+
383
+ upgradeOperation.setPhase('pin-binary', { binaryPath: installedPath });
384
+ if (typeof forceResolvedOpenCodeBinary !== 'function') {
385
+ const error = new Error('Owned-cache upgrade requires forceResolvedOpenCodeBinary');
386
+ error.code = 'UPGRADE_FORCE_BINARY_UNAVAILABLE';
387
+ throw error;
388
+ }
389
+ // Keep target identity through restart — do not rediscover global CLI.
390
+ forceResolvedOpenCodeBinary(installedPath, 'installed');
391
+
392
+ upgradeOperation.setPhase('restart');
393
+ if (typeof restartOpenCode === 'function') {
394
+ await restartOpenCode(ownership.ownership === 'shared-service' ? { binaryPath: installedPath } : undefined);
395
+ } else if (typeof refreshOpenCodeAfterConfigChange === 'function') {
396
+ // Fallback: still pin binary first so restart prefers owned cache.
397
+ await refreshOpenCodeAfterConfigChange('OpenCode owned-cache upgrade');
398
+ } else {
399
+ const error = new Error('No OpenCode restart path is configured');
400
+ error.code = 'UPGRADE_RESTART_UNAVAILABLE';
401
+ throw error;
402
+ }
403
+
404
+ if (typeof waitForOpenCodeReady === 'function') {
405
+ upgradeOperation.setPhase('wait-ready');
406
+ await waitForOpenCodeReady();
407
+ }
408
+
409
+ upgradeOperation.setPhase('verify');
410
+ // Re-pin after restart helpers that may clear resolution.
411
+ forceResolvedOpenCodeBinary(installedPath, 'installed');
412
+ const contract = await buildLiveContract({ fresh: true });
413
+ const verification = evaluateOwnedUpgradeResult({
414
+ targetVersion: target,
415
+ serveVersion: contract.serveVersion,
416
+ cliVersion: contract.cliVersion,
417
+ binaryPath: typeof getResolvedOpenCodeBinary === 'function'
418
+ ? getResolvedOpenCodeBinary()
419
+ : installedPath,
420
+ expectedBinaryPath: installedPath,
421
+ contract,
422
+ });
423
+ if (!verification.ok) {
424
+ const error = new Error(verification.error);
425
+ error.code = verification.errorCode;
426
+ error.serveVersion = verification.serveVersion;
427
+ error.contract = verification.contract;
428
+ throw error;
429
+ }
430
+
431
+ upgradeOperation.succeed({
432
+ serveVersion: verification.serveVersion,
433
+ binaryPath: installedPath,
434
+ });
435
+ return res.json({
436
+ success: true,
437
+ upgraded: true,
438
+ restarted: true,
439
+ version: verification.serveVersion,
440
+ targetVersion: target,
441
+ pinned: true,
442
+ supplySource: 'owned-cache',
443
+ ownership: ownership.ownership === 'shared-service' ? 'shared-service' : 'owned-cache',
444
+ binaryPath: installedPath,
445
+ contract: verification.contract,
446
+ operation: upgradeOperation.getState(),
447
+ });
448
+ } catch (upgradeError) {
449
+ if (previousBinary && typeof forceResolvedOpenCodeBinary === 'function') {
450
+ forceResolvedOpenCodeBinary(previousBinary, previousSource);
451
+ }
452
+ const message = upgradeError instanceof Error ? upgradeError.message : 'Failed to upgrade OpenCode';
453
+ const errorCode = upgradeError?.code || 'UPGRADE_FAILED';
454
+ upgradeOperation.fail({
455
+ error: message,
456
+ errorCode,
457
+ phase: upgradeOperation.getState().phase,
458
+ serveVersion: upgradeError?.serveVersion ?? null,
459
+ binaryPath: installedPath,
460
+ });
461
+ // Preserve previous owned cache on disk; only report failure.
216
462
  return res.status(500).json({
217
463
  success: false,
218
- upgraded: true,
219
- error: restartError instanceof Error
220
- ? `OpenCode upgraded, but restart failed: ${restartError.message}`
221
- : 'OpenCode upgraded, but restart failed',
464
+ upgraded: false,
465
+ error: message,
466
+ errorCode,
467
+ targetVersion: target,
468
+ operation: upgradeOperation.getState(),
469
+ contract: upgradeError?.contract || null,
222
470
  });
223
471
  }
224
-
225
- return res.json({ success: true, restarted: true, version: target, pinned: true });
226
472
  } catch (error) {
227
473
  console.error('Failed to upgrade OpenCode:', error);
228
474
  return res.status(500).json({
@@ -234,42 +480,35 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
234
480
 
235
481
  app.get('/api/opencode/upgrade-status', async (_req, res) => {
236
482
  try {
237
- if (await isBundledOpenCodeBinaryActive()) {
238
- const current = await readOpenCodeCurrentVersion().catch(() => ({ ok: false, currentVersion: null }));
239
- return res.json({
240
- available: false,
241
- currentVersion: current.ok ? current.currentVersion : null,
242
- latestVersion: null,
243
- source: 'bundled',
244
- });
245
- }
246
-
247
- const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
248
- method: 'GET',
249
- headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
483
+ const status = await buildUpgradeStatus();
484
+ return res.json(status);
485
+ } catch (error) {
486
+ return res.status(500).json({
487
+ available: null,
488
+ error: error instanceof Error ? error.message : 'Failed to check OpenCode upgrade status',
489
+ operation: upgradeOperation.getState(),
250
490
  });
251
- const health = await healthResponse.json().catch(() => null);
252
- if (!healthResponse.ok) {
253
- return res.status(healthResponse.status).json({
254
- available: null,
255
- error: health?.error || healthResponse.statusText || 'Failed to read OpenCode version',
256
- });
257
- }
258
- const currentVersion = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
259
- const latestVersion = isOpenCode1xVersion(PINNED_OPENCODE2_VERSION) ? null : PINNED_OPENCODE2_VERSION;
260
- if (!currentVersion || !latestVersion) {
261
- return res.json({ available: null, currentVersion, latestVersion });
262
- }
263
- const available = !isOpenCode1xVersion(currentVersion) && compareVersions(latestVersion, currentVersion) > 0;
491
+ }
492
+ });
493
+
494
+ app.get('/api/opencode/contract', async (_req, res) => {
495
+ try {
496
+ const contract = await buildLiveContract();
497
+ const ownership = await resolveOwnershipContext();
264
498
  return res.json({
265
- available,
266
- currentVersion,
267
- latestVersion,
499
+ ...contract,
500
+ ownership: ownership.ownership,
501
+ supplySource: ownership.supplySource,
502
+ canManageUpgrade: ownership.canUpgradeInApp,
503
+ management: ownership.management,
504
+ guidance: ownership.guidance,
268
505
  });
269
506
  } catch (error) {
270
507
  return res.status(500).json({
271
- available: null,
272
- error: error instanceof Error ? error.message : 'Failed to check OpenCode upgrade status',
508
+ schemaVersion: 1,
509
+ phase: 'unknown',
510
+ executionAllowed: false,
511
+ error: error instanceof Error ? error.message : 'Failed to evaluate OpenCode runtime contract',
273
512
  });
274
513
  }
275
514
  });
@@ -283,22 +522,45 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
283
522
  });
284
523
  const health = await healthResponse.json().catch(() => null);
285
524
  if (!healthResponse.ok) {
525
+ const contract = evaluateRuntimeContract({
526
+ reachable: false,
527
+ authenticated: healthResponse.status === 401 || healthResponse.status === 403 ? false : null,
528
+ healthOk: false,
529
+ });
286
530
  return res.status(healthResponse.status).json({
287
531
  healthy: false,
288
532
  error: health?.error || healthResponse.statusText || 'OpenCode health check failed',
533
+ contract,
289
534
  });
290
535
  }
291
- return res.json({ healthy: health?.healthy === true });
536
+ // Reuse lifecycle/sidecar gate: official 2.x ServerInfo omits `healthy`.
537
+ const gate = evaluateOpenCodeHealthBody(health);
538
+ const contract = await buildLiveContract().catch(() => evaluateRuntimeContract({
539
+ serveVersion: gate.version,
540
+ reachable: gate.ok,
541
+ authenticated: true,
542
+ healthOk: gate.ok,
543
+ }));
544
+ return res.json({
545
+ healthy: gate.ok,
546
+ version: gate.version,
547
+ // Healthy is reachability/version shape only — not full execution semantics.
548
+ executionAllowed: contract.executionAllowed === true,
549
+ protocolCompatible: contract.protocolCompatible === true,
550
+ contract,
551
+ });
292
552
  } catch (error) {
293
553
  return res.status(503).json({
294
554
  healthy: false,
295
555
  error: error instanceof Error ? error.message : 'OpenCode health check failed',
556
+ contract: evaluateRuntimeContract({ reachable: false, healthOk: false }),
296
557
  });
297
558
  }
298
559
  });
299
560
 
300
561
  app.get('/api/opencode/version', async (_req, res) => {
301
562
  try {
563
+ const contract = await buildLiveContract();
302
564
  const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
303
565
  method: 'GET',
304
566
  headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
@@ -307,11 +569,20 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
307
569
  if (!healthResponse.ok) {
308
570
  return res.status(healthResponse.status).json({
309
571
  version: null,
572
+ serveVersion: null,
573
+ cliVersion: contract.cliVersion,
310
574
  error: health?.error || healthResponse.statusText || 'Failed to read OpenCode version',
575
+ contract,
311
576
  });
312
577
  }
313
578
  const version = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
314
- return res.json({ version });
579
+ return res.json({
580
+ version,
581
+ serveVersion: version,
582
+ cliVersion: contract.cliVersion,
583
+ versionMismatch: Boolean(version && contract.cliVersion && version !== contract.cliVersion),
584
+ contract,
585
+ });
315
586
  } catch (error) {
316
587
  return res.status(500).json({
317
588
  version: null,
@@ -599,13 +870,6 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
599
870
 
600
871
  await fs.promises.writeFile(AGENTS_MD_PATH, content, 'utf8');
601
872
 
602
- // Refresh OpenCode so it picks up the new AGENTS.md without a full restart
603
- try {
604
- await refreshOpenCodeAfterConfigChange('global behavior (AGENTS.md) updated');
605
- } catch {
606
- // Non-fatal: file was written successfully
607
- }
608
-
609
873
  return res.json({ success: true });
610
874
  } catch (error) {
611
875
  console.error('Failed to write AGENTS.md:', error);