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

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 (287) 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-Q1SmFRx2.js} +1 -1
  7. package/dist/assets/AssistantView-CwXkiCAd.js +3 -0
  8. package/dist/assets/{DiagramView-xuSJTnv7.js → DiagramView-BW92schO.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-Ov0D96d0.js → FadeInOnReveal-BZmwl-zE.js} +1 -1
  10. package/dist/assets/{Icon-DD-BuMfZ.js → Icon-AQPvxMiU.js} +1 -1
  11. package/dist/assets/{MarkdownRenderer-bc_p1H7l.js → MarkdownRenderer-Bv05iDZZ.js} +2 -2
  12. package/dist/assets/{MarkdownRendererImpl-SKi4oG0c.js → MarkdownRendererImpl-C81xqYib.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-CXUCQuKJ.js +3 -0
  14. package/dist/assets/{MobileDetailNavigation-Bnx59U_J.js → MobileDetailNavigation-BMgIu7zc.js} +1 -1
  15. package/dist/assets/{MobileShareBridge-BQlP4J-F.js → MobileShareBridge-BOXGNHmC.js} +2 -2
  16. package/dist/assets/MobileSurface-xuc_z0_G.js +2 -0
  17. package/dist/assets/{MultiRunWindow-ChZGob0v.js → MultiRunWindow-B5At2Z95.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-DtQJxZG-.js → OnboardingScreen-bsPLgz4r.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-4wp4migh.js +2 -0
  20. package/dist/assets/SettingsView-CJ6tcH5_.js +25 -0
  21. package/dist/assets/{SettingsWindow-DfnsFa8y.js → SettingsWindow-CMtX2bMl.js} +1 -1
  22. package/dist/assets/{TerminalView-Dh9MWCpS.js → TerminalView-Dvr-ysTS.js} +1 -1
  23. package/dist/assets/{ToolOutputDialog-BejmjrRk.js → ToolOutputDialog-uH1ZZDSS.js} +2 -2
  24. package/dist/assets/appThemeRegistry-DK7q2juD.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-CcNJUxUd.js} +1 -1
  27. package/dist/assets/collapsible-ZmZ6rLWs.js +3 -0
  28. package/dist/assets/context-menu-B2LXJgXL.js +1 -0
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-C6HrP0jq.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DdVBp45U.js} +1 -1
  31. package/dist/assets/{diagram-DQf7Y_uR.js → diagram-CxTs9hCQ.js} +1 -1
  32. package/dist/assets/{dialog-bfbvR4VH.js → dialog-KMVL9QKF.js} +1 -1
  33. package/dist/assets/es-CN3E_zXj.js +15 -0
  34. package/dist/assets/{fileMentionTouchSelection-BvrNCroG.js → fileMentionTouchSelection-7RIwlpkB.js} +1 -1
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-CjdBRy8y.js} +1 -1
  36. package/dist/assets/fr-9zZvQZD2.js +13 -0
  37. package/dist/assets/gitApi-yuWg9BN4.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-9vQBq3zp.js} +1 -1
  39. package/dist/assets/{input-8r6F_Fza.js → input-CrIvk3HZ.js} +1 -1
  40. package/dist/assets/insertionBoundaries-BiUCSkx-.js +1 -0
  41. package/dist/assets/ja-Dnjk_82N.js +15 -0
  42. package/dist/assets/ko-BxCEE99C.js +15 -0
  43. package/dist/assets/{main-CCE_4y5F.js → main-C9mIBo77.js} +2 -2
  44. package/dist/assets/main-DiL6w6KF.js +169 -0
  45. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  46. package/dist/assets/{markdownHeightCache-1Qo6y6IJ.js → markdownHeightCache-BmXVY_gc.js} +1 -1
  47. package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
  48. package/dist/assets/mcpQueries-C7jknHV8.js +1 -0
  49. package/dist/assets/miniChat-CwtUaElU.js +2 -0
  50. package/dist/assets/mobile-DB-6--ws.js +2 -0
  51. package/dist/assets/multirun-TAsxFI8e.js +4 -0
  52. package/dist/assets/{persistence-Ce3AnpEr.js → persistence-56kq8Z4V.js} +2 -2
  53. package/dist/assets/pl-CNNsWnPM.js +15 -0
  54. package/dist/assets/platform-C13fMVEs.js +6 -0
  55. package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
  56. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  57. package/dist/assets/renderElectronMiniChatApp-CGj0MNRU.js +2 -0
  58. package/dist/assets/renderMobileApp-CGMMqbsO.js +28 -0
  59. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  60. package/dist/assets/{runtimeConfig-BMvqa5we.js → runtimeConfig-BEANkvOd.js} +1 -1
  61. package/dist/assets/runtimeEndpointReset-A_ZBUJFS.js +147 -0
  62. package/dist/assets/sessionChangeSummary-qOLrP9fw.js +2 -0
  63. package/dist/assets/{sessionLookup-gh-hVRIc.js → sessionLookup-34XFmP9C.js} +1 -1
  64. package/dist/assets/{sortable-tabs-strip-CudoSgV4.js → sortable-tabs-strip-DyEV0Heg.js} +1 -1
  65. package/dist/assets/src-ubCWXZhR.css +2 -0
  66. package/dist/assets/store-DLCiZaA6.js +15 -0
  67. package/dist/assets/{terminalApi-DiF6JmBP.js → terminalApi-CXaBEoa_.js} +1 -1
  68. package/dist/assets/textarea-BBOlRI9O.js +1 -0
  69. package/dist/assets/{tooltip-CQvpK-UV.js → tooltip-CPI4fr1a.js} +1 -1
  70. package/dist/assets/uk-rwQrxeYM.js +15 -0
  71. package/dist/assets/{url-_z7evzdK.js → url-DRDQXjyX.js} +1 -1
  72. package/dist/assets/{useAppFontEffects-Cpmpo7Wb.js → useAppFontEffects-CiDe42I5.js} +57 -43
  73. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
  74. package/dist/assets/useDesktopWindowControlsLayout-BlizAlOt.js +1 -0
  75. package/dist/assets/useEffectiveDirectory-C_CouL5v.js +1 -0
  76. package/dist/assets/{useFontPreferences-DcbXQOkc.js → useFontPreferences-B0oUZAiB.js} +1 -1
  77. package/dist/assets/useMobileNavigationStore-DfPClaW-.js +414 -0
  78. package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
  79. package/dist/assets/useSessionAutoCleanup-DtrfbxAp.js +2 -0
  80. package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
  81. package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-Bqn4AiBL.js → useWorkerHighlightedLines-CPNYz6Wf.js} +2 -2
  83. package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
  84. package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
  85. package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
  86. package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
  87. package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
  88. package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
  89. package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
  90. package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
  91. package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
  92. package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
  93. package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
  94. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
  95. package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
  96. package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
  97. package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
  98. package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
  99. package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
  100. package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
  101. package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
  102. package/dist/index.html +13 -13
  103. package/dist/mini-chat.html +13 -13
  104. package/dist/mobile.html +13 -13
  105. package/package.json +1 -1
  106. package/server/index.js +327 -62
  107. package/server/lib/assistants/DOCUMENTATION.md +14 -9
  108. package/server/lib/assistants/contracts.js +11 -1
  109. package/server/lib/assistants/projection-history.test.js +265 -18
  110. package/server/lib/assistants/routes.js +6 -0
  111. package/server/lib/assistants/service.js +417 -231
  112. package/server/lib/assistants/service.test.js +392 -148
  113. package/server/lib/conversations/DOCUMENTATION.md +1 -1
  114. package/server/lib/conversations/service.js +1 -0
  115. package/server/lib/conversations/service.test.js +16 -0
  116. package/server/lib/conversations/validation.js +11 -1
  117. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  118. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  119. package/server/lib/event-stream/global-hub.js +49 -4
  120. package/server/lib/event-stream/reasoning-projection.js +16 -1
  121. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  122. package/server/lib/event-stream/runtime.js +57 -20
  123. package/server/lib/event-stream/runtime.test.js +112 -0
  124. package/server/lib/llm/DOCUMENTATION.md +33 -9
  125. package/server/lib/llm/catalog.js +1 -1
  126. package/server/lib/llm/completions.js +5 -1
  127. package/server/lib/llm/completions.test.js +3 -3
  128. package/server/lib/llm/generate.js +237 -145
  129. package/server/lib/llm/generate.test.js +201 -46
  130. package/server/lib/llm/routes.js +2 -0
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/notifications/runtime.js +23 -5
  133. package/server/lib/notifications/runtime.test.js +11 -0
  134. package/server/lib/opencode/DOCUMENTATION.md +85 -17
  135. package/server/lib/opencode/agent-document.d.ts +28 -0
  136. package/server/lib/opencode/agent-document.js +280 -0
  137. package/server/lib/opencode/agent-document.test.js +137 -0
  138. package/server/lib/opencode/agents.js +96 -0
  139. package/server/lib/opencode/auth-state-runtime.js +5 -0
  140. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  141. package/server/lib/opencode/config-entity-routes.js +42 -99
  142. package/server/lib/opencode/config-entity-routes.test.js +107 -0
  143. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  144. package/server/lib/opencode/ensure-cli.js +1 -2
  145. package/server/lib/opencode/env-runtime.js +40 -26
  146. package/server/lib/opencode/env-runtime.test.js +10 -17
  147. package/server/lib/opencode/feature-routes-runtime.js +43 -4
  148. package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
  149. package/server/lib/opencode/http-server-connections.js +98 -0
  150. package/server/lib/opencode/http-server-connections.test.js +321 -0
  151. package/server/lib/opencode/lifecycle.js +349 -39
  152. package/server/lib/opencode/lifecycle.test.js +322 -0
  153. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  154. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  155. package/server/lib/opencode/openchamber-routes.js +7 -1
  156. package/server/lib/opencode/opencode2-pin.js +4 -4
  157. package/server/lib/opencode/opencode2-pin.test.js +1 -0
  158. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +127 -4
  159. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  160. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  161. package/server/lib/opencode/plugin-routes.js +3 -18
  162. package/server/lib/opencode/plugin-routes.test.js +9 -9
  163. package/server/lib/opencode/provider-catalog.js +58 -12
  164. package/server/lib/opencode/provider-catalog.test.js +48 -0
  165. package/server/lib/opencode/proxy.js +252 -46
  166. package/server/lib/opencode/proxy.test.js +235 -2
  167. package/server/lib/opencode/routes.js +296 -62
  168. package/server/lib/opencode/routes.test.js +278 -12
  169. package/server/lib/opencode/runtime-contract.js +401 -0
  170. package/server/lib/opencode/runtime-contract.test.js +214 -0
  171. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  172. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  173. package/server/lib/opencode/server-utils-runtime.js +33 -0
  174. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  175. package/server/lib/opencode/shared-service.js +147 -0
  176. package/server/lib/opencode/shared-service.test.js +93 -0
  177. package/server/lib/opencode/shared.js +3 -3
  178. package/server/lib/opencode/shutdown-runtime.js +15 -6
  179. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  180. package/server/lib/opencode/skill-routes.js +20 -33
  181. package/server/lib/opencode/skill-routes.test.js +35 -2
  182. package/server/lib/opencode/skills.test.js +15 -0
  183. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  184. package/server/lib/opencode/static-routes-runtime.js +3 -1
  185. package/server/lib/opencode/static-routes-runtime.test.js +33 -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 +61 -16
  211. package/server/lib/session-metadata/routes.js +108 -9
  212. package/server/lib/session-metadata/routes.test.js +47 -2
  213. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  214. package/server/lib/session-metadata/session-archive.js +654 -0
  215. package/server/lib/session-metadata/session-archive.test.js +359 -0
  216. package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
  217. package/server/lib/session-metadata/session-metadata-store.js +212 -72
  218. package/server/lib/session-metadata/session-metadata-store.test.js +205 -10
  219. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  220. package/server/lib/session-metadata/session-projection.js +255 -0
  221. package/server/lib/session-metadata/session-projection.test.js +64 -0
  222. package/server/lib/session-metadata/system-session.js +55 -0
  223. package/server/lib/session-metadata/system-session.test.js +55 -0
  224. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  225. package/server/lib/session-title/native-runtime.test.js +167 -0
  226. package/server/lib/session-title/runtime.js +67 -42
  227. package/server/lib/session-title/session-access.js +46 -0
  228. package/server/lib/session-title/session-access.test.js +91 -0
  229. package/server/lib/session-turn-pages/DOCUMENTATION.md +9 -8
  230. package/server/lib/session-turn-pages/routes.js +17 -102
  231. package/server/lib/session-turn-pages/routes.test.js +281 -0
  232. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  233. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  234. package/server/lib/small-model/DOCUMENTATION.md +101 -147
  235. package/server/lib/small-model/catalog.js +31 -51
  236. package/server/lib/small-model/catalog.test.js +65 -10
  237. package/server/lib/small-model/custom-api.js +68 -0
  238. package/server/lib/small-model/custom-api.test.js +64 -0
  239. package/server/lib/small-model/index.js +124 -240
  240. package/server/lib/small-model/resolve.js +35 -120
  241. package/server/lib/small-model/resolve.test.js +20 -110
  242. package/server/lib/small-model/routes.js +1 -3
  243. package/server/lib/small-model/summary-settings.test.js +157 -253
  244. package/server/opencode-proxy.test.js +2 -0
  245. package/dist/assets/AssistantView-BTCkm8yk.js +0 -3
  246. package/dist/assets/MarkstreamRendererImpl-CnkodGJW.js +0 -3
  247. package/dist/assets/MobileSurface-BXLeiZ4d.js +0 -2
  248. package/dist/assets/OverlayScrollbar-pMwxaCca.js +0 -2
  249. package/dist/assets/SettingsView-B5I0KDTZ.js +0 -25
  250. package/dist/assets/appThemeRegistry-COGfxTFV.js +0 -1
  251. package/dist/assets/collapsible-BXRd5_Hr.js +0 -1
  252. package/dist/assets/context-menu-jGtH6fR9.js +0 -1
  253. package/dist/assets/es-c52LTHHe.js +0 -15
  254. package/dist/assets/fr-PisFbUE8.js +0 -13
  255. package/dist/assets/gitApi-ButwzQ0N.js +0 -9
  256. package/dist/assets/insertionBoundaries-DGs7S5zv.js +0 -1
  257. package/dist/assets/ja-C8Ikun3Z.js +0 -15
  258. package/dist/assets/ko-ClFys5o2.js +0 -15
  259. package/dist/assets/main-Dn9rpsgl.js +0 -169
  260. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  261. package/dist/assets/miniChat-BpjxVqgg.js +0 -2
  262. package/dist/assets/mobile-nMJCaNq1.js +0 -2
  263. package/dist/assets/multirun-BvSG94mW.js +0 -4
  264. package/dist/assets/pl-CnaKWN5F.js +0 -15
  265. package/dist/assets/platform-CWTIp02q.js +0 -4
  266. package/dist/assets/projectMeta-DOe49dM_.js +0 -3
  267. package/dist/assets/pt-BR-s8OpfRI6.js +0 -15
  268. package/dist/assets/renderElectronMiniChatApp-R6dKh79f.js +0 -2
  269. package/dist/assets/renderMobileApp-y-S8y8v_.js +0 -28
  270. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  271. package/dist/assets/runtimeEndpointReset-B6BxhvR0.js +0 -147
  272. package/dist/assets/sessionChangeSummary-VVBlEXuh.js +0 -2
  273. package/dist/assets/src-BzX5UI-c.css +0 -2
  274. package/dist/assets/store-DJczmczv.js +0 -15
  275. package/dist/assets/textarea-3XR8sUfy.js +0 -1
  276. package/dist/assets/uk-CaX0iqkB.js +0 -15
  277. package/dist/assets/useDesktopWindowControlsLayout-B1GX59-L.js +0 -1
  278. package/dist/assets/useEffectiveDirectory-CN6JoCFw.js +0 -1
  279. package/dist/assets/useMobileNavigationStore-BVjFtXSy.js +0 -414
  280. package/dist/assets/useSessionAutoCleanup-BLjymaFt.js +0 -2
  281. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  282. package/dist/assets/zh-CN-20MYEjha.js +0 -15
  283. package/dist/assets/zh-TW-DiN04qTw.js +0 -15
  284. package/server/lib/small-model/call.js +0 -427
  285. package/server/lib/small-model/call.test.js +0 -382
  286. package/server/lib/small-model/opencode-session.js +0 -183
  287. package/server/lib/small-model/opencode-session.test.js +0 -147
@@ -1,6 +1,24 @@
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 { evaluateRuntimeContract } from './runtime-contract.js';
15
+ import {
16
+ buildUpgradeStatusSnapshot,
17
+ classifyRuntimeOwnership,
18
+ createUpgradeOperationState,
19
+ evaluateOwnedUpgradeResult,
20
+ resolveOwnedCacheBinaryPath,
21
+ } from './owned-runtime-upgrade.js';
4
22
  import fs from 'fs';
5
23
  import os from 'os';
6
24
  import path from 'path';
@@ -24,8 +42,22 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
24
42
  getOpenCodeAuthHeaders,
25
43
  onSettingsPersisted,
26
44
  getIsExternalOpenCode = () => false,
45
+ getIsSharedOpenCodeService = () => false,
46
+ forceResolvedOpenCodeBinary = null,
47
+ restartOpenCode = null,
48
+ waitForOpenCodeReady = null,
49
+ getRuntimeContract = () => null,
50
+ getOpenCodeServeVersion = () => null,
51
+ getOpenCodeCliVersion = () => null,
52
+ getResolvedOpenCodeBinary = () => null,
53
+ getResolvedOpenCodeBinarySource = () => null,
54
+ getActiveSessionCount = () => 0,
55
+ openchamberDataDir = null,
27
56
  } = dependencies;
28
57
 
58
+ const upgradeOperation = createUpgradeOperationState();
59
+ const resolveDataDir = () => openchamberDataDir || resolveOpenChamberDataDir();
60
+
29
61
  let authLibrary = null;
30
62
  const pendingMcpAuthContextByState = new Map();
31
63
  const PENDING_MCP_AUTH_TTL_MS = 30 * 60 * 1000;
@@ -58,10 +90,81 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
58
90
  });
59
91
  const health = await healthResponse.json().catch(() => null);
60
92
  if (!healthResponse.ok) {
61
- return { ok: false, status: healthResponse.status, error: health?.error || healthResponse.statusText };
93
+ return {
94
+ ok: false,
95
+ status: healthResponse.status,
96
+ error: health?.error || healthResponse.statusText,
97
+ authenticated: healthResponse.status === 401 || healthResponse.status === 403 ? false : null,
98
+ };
62
99
  }
63
- const currentVersion = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
64
- return { ok: true, currentVersion };
100
+ const gate = evaluateOpenCodeHealthBody(health);
101
+ const currentVersion = gate.version
102
+ || (typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null);
103
+ return { ok: gate.ok, currentVersion, healthOk: gate.ok, authenticated: true };
104
+ };
105
+
106
+ const resolveOwnershipContext = async () => {
107
+ const isExternal = getIsExternalOpenCode() === true;
108
+ let binarySource = typeof getResolvedOpenCodeBinarySource === 'function'
109
+ ? getResolvedOpenCodeBinarySource()
110
+ : null;
111
+ let binaryPath = typeof getResolvedOpenCodeBinary === 'function'
112
+ ? getResolvedOpenCodeBinary()
113
+ : null;
114
+ try {
115
+ const settings = await readSettingsFromDiskMigrated();
116
+ const resolution = await getOpenCodeResolutionSnapshot(settings);
117
+ binarySource = binarySource || resolution?.source || resolution?.detectedSourceNow || null;
118
+ binaryPath = binaryPath || resolution?.resolved || null;
119
+ if (resolution?.source === 'bundled' || resolution?.detectedSourceNow === 'bundled') {
120
+ binarySource = 'bundled';
121
+ }
122
+ } catch {
123
+ // Resolution failures still allow ownership classification from runtime flags.
124
+ }
125
+ return classifyRuntimeOwnership({
126
+ isExternal,
127
+ isSharedService: getIsSharedOpenCodeService() === true,
128
+ binarySource,
129
+ binaryPath,
130
+ dataDir: resolveDataDir(),
131
+ });
132
+ };
133
+
134
+ const buildLiveContract = async ({ fresh = false } = {}) => {
135
+ const cached = typeof getRuntimeContract === 'function' ? getRuntimeContract() : null;
136
+ const serveProbe = await readOpenCodeCurrentVersion().catch(() => ({ ok: false, currentVersion: null }));
137
+ const serveVersion = serveProbe.currentVersion
138
+ || (!fresh && typeof getOpenCodeServeVersion === 'function' ? getOpenCodeServeVersion() : null);
139
+ const binaryPath = typeof getResolvedOpenCodeBinary === 'function' ? getResolvedOpenCodeBinary() : null;
140
+ const cliVersion = (typeof getOpenCodeCliVersion === 'function' ? getOpenCodeCliVersion() : null)
141
+ || (binaryPath ? readOpenCode2BinaryVersion(binaryPath) : null)
142
+ || null;
143
+ return evaluateRuntimeContract({
144
+ serveVersion,
145
+ cliVersion,
146
+ reachable: fresh ? serveProbe.ok === true : serveProbe.ok === true || Boolean(serveVersion) || Boolean(cached?.reachable),
147
+ authenticated: serveProbe.authenticated ?? (fresh ? null : cached?.authenticated) ?? null,
148
+ healthOk: serveProbe.healthOk ?? serveProbe.ok ?? (fresh ? false : cached?.healthOk) ?? null,
149
+ migrationAdmitTranscript: cached?.migrationExecutable ?? null,
150
+ migrationPhase: cached?.migrationPhase ?? null,
151
+ migrationError: cached?.migrationError ?? null,
152
+ });
153
+ };
154
+
155
+ const buildUpgradeStatus = async () => {
156
+ const ownership = await resolveOwnershipContext();
157
+ const contract = await buildLiveContract();
158
+ const activeCount = typeof getActiveSessionCount === 'function' ? Number(getActiveSessionCount()) || 0 : 0;
159
+ return buildUpgradeStatusSnapshot({
160
+ ownership,
161
+ serveVersion: contract.serveVersion,
162
+ cliVersion: contract.cliVersion,
163
+ targetVersion: PINNED_OPENCODE2_VERSION,
164
+ contract,
165
+ operation: upgradeOperation.getState(),
166
+ hasActiveTasks: activeCount > 0,
167
+ });
65
168
  };
66
169
 
67
170
  const parseVersionForComparison = (value) => {
@@ -185,17 +288,17 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
185
288
 
186
289
  app.post('/api/opencode/upgrade', async (req, res) => {
187
290
  try {
188
- if (getIsExternalOpenCode()) {
189
- return res.status(409).json({
291
+ const ownership = await resolveOwnershipContext();
292
+ if (!ownership.canUpgradeInApp) {
293
+ const status = ownership.ownership === 'external-serve' || ownership.ownership === 'bundled' ? 409 : 409;
294
+ return res.status(status).json({
190
295
  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({
197
- success: false,
198
- error: 'OpenCode is bundled with OpenChamber Desktop and cannot be upgraded separately.',
296
+ error: ownership.guidance
297
+ || 'OpenChamber can only upgrade its owned OpenCode cache in-app.',
298
+ errorCode: ownership.reason || 'UPGRADE_NOT_MANAGED',
299
+ ownership: ownership.ownership,
300
+ management: ownership.management,
301
+ guidance: ownership.guidance,
199
302
  });
200
303
  }
201
304
 
@@ -206,23 +309,136 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
206
309
  return res.status(400).json({
207
310
  success: false,
208
311
  error: 'OpenCode upgrade refuses 1.x targets. Only pinned opencode2 is allowed.',
312
+ errorCode: 'OPENCODE_UPGRADE_1X_REFUSED',
209
313
  });
210
314
  }
211
315
  const target = resolveOpenCode2UpgradeTarget(rawTarget);
316
+ const confirmActive = req.body?.confirmActiveTasks === true || req.body?.force === true;
317
+ const activeCount = typeof getActiveSessionCount === 'function' ? Number(getActiveSessionCount()) || 0 : 0;
318
+ if (activeCount > 0 && !confirmActive) {
319
+ return res.status(409).json({
320
+ success: false,
321
+ error: 'OpenCode has active sessions. Confirm upgrade to interrupt them, or wait until idle.',
322
+ errorCode: 'UPGRADE_ACTIVE_TASKS',
323
+ hasActiveTasks: true,
324
+ activeSessionCount: activeCount,
325
+ });
326
+ }
327
+
328
+ const begin = upgradeOperation.begin(target);
329
+ if (!begin.ok) {
330
+ return res.status(begin.status).json(begin.body);
331
+ }
332
+
333
+ const expectedBinaryPath = resolveOwnedCacheBinaryPath(target, { dataDir: resolveDataDir() });
334
+ let installedPath = expectedBinaryPath;
335
+ const previousBinary = getResolvedOpenCodeBinary();
336
+ const previousSource = getResolvedOpenCodeBinarySource();
212
337
 
213
338
  try {
214
- await refreshOpenCodeAfterConfigChange('OpenCode upgrade');
215
- } catch (restartError) {
339
+ upgradeOperation.setPhase('download');
340
+ installedPath = await installPinnedOpenCode2Cli({
341
+ version: target,
342
+ dataDir: resolveDataDir(),
343
+ });
344
+ const diskVersion = readOpenCode2BinaryVersion(installedPath);
345
+ if (diskVersion !== target) {
346
+ const error = new Error(
347
+ `Owned-cache binary version mismatch after install: expected ${target}, got ${diskVersion || 'unknown'}`,
348
+ );
349
+ error.code = 'UPGRADE_BINARY_VERSION_MISMATCH';
350
+ throw error;
351
+ }
352
+
353
+ upgradeOperation.setPhase('pin-binary', { binaryPath: installedPath });
354
+ if (typeof forceResolvedOpenCodeBinary !== 'function') {
355
+ const error = new Error('Owned-cache upgrade requires forceResolvedOpenCodeBinary');
356
+ error.code = 'UPGRADE_FORCE_BINARY_UNAVAILABLE';
357
+ throw error;
358
+ }
359
+ // Keep target identity through restart — do not rediscover global CLI.
360
+ forceResolvedOpenCodeBinary(installedPath, 'installed');
361
+
362
+ upgradeOperation.setPhase('restart');
363
+ if (typeof restartOpenCode === 'function') {
364
+ await restartOpenCode(ownership.ownership === 'shared-service' ? { binaryPath: installedPath } : undefined);
365
+ } else if (typeof refreshOpenCodeAfterConfigChange === 'function') {
366
+ // Fallback: still pin binary first so restart prefers owned cache.
367
+ await refreshOpenCodeAfterConfigChange('OpenCode owned-cache upgrade');
368
+ } else {
369
+ const error = new Error('No OpenCode restart path is configured');
370
+ error.code = 'UPGRADE_RESTART_UNAVAILABLE';
371
+ throw error;
372
+ }
373
+
374
+ if (typeof waitForOpenCodeReady === 'function') {
375
+ upgradeOperation.setPhase('wait-ready');
376
+ await waitForOpenCodeReady();
377
+ }
378
+
379
+ upgradeOperation.setPhase('verify');
380
+ // Re-pin after restart helpers that may clear resolution.
381
+ forceResolvedOpenCodeBinary(installedPath, 'installed');
382
+ const contract = await buildLiveContract({ fresh: true });
383
+ const verification = evaluateOwnedUpgradeResult({
384
+ targetVersion: target,
385
+ serveVersion: contract.serveVersion,
386
+ cliVersion: contract.cliVersion,
387
+ binaryPath: typeof getResolvedOpenCodeBinary === 'function'
388
+ ? getResolvedOpenCodeBinary()
389
+ : installedPath,
390
+ expectedBinaryPath: installedPath,
391
+ contract,
392
+ });
393
+ if (!verification.ok) {
394
+ const error = new Error(verification.error);
395
+ error.code = verification.errorCode;
396
+ error.serveVersion = verification.serveVersion;
397
+ error.contract = verification.contract;
398
+ throw error;
399
+ }
400
+
401
+ upgradeOperation.succeed({
402
+ serveVersion: verification.serveVersion,
403
+ binaryPath: installedPath,
404
+ });
405
+ return res.json({
406
+ success: true,
407
+ upgraded: true,
408
+ restarted: true,
409
+ version: verification.serveVersion,
410
+ targetVersion: target,
411
+ pinned: true,
412
+ supplySource: 'owned-cache',
413
+ ownership: ownership.ownership === 'shared-service' ? 'shared-service' : 'owned-cache',
414
+ binaryPath: installedPath,
415
+ contract: verification.contract,
416
+ operation: upgradeOperation.getState(),
417
+ });
418
+ } catch (upgradeError) {
419
+ if (previousBinary && typeof forceResolvedOpenCodeBinary === 'function') {
420
+ forceResolvedOpenCodeBinary(previousBinary, previousSource);
421
+ }
422
+ const message = upgradeError instanceof Error ? upgradeError.message : 'Failed to upgrade OpenCode';
423
+ const errorCode = upgradeError?.code || 'UPGRADE_FAILED';
424
+ upgradeOperation.fail({
425
+ error: message,
426
+ errorCode,
427
+ phase: upgradeOperation.getState().phase,
428
+ serveVersion: upgradeError?.serveVersion ?? null,
429
+ binaryPath: installedPath,
430
+ });
431
+ // Preserve previous owned cache on disk; only report failure.
216
432
  return res.status(500).json({
217
433
  success: false,
218
- upgraded: true,
219
- error: restartError instanceof Error
220
- ? `OpenCode upgraded, but restart failed: ${restartError.message}`
221
- : 'OpenCode upgraded, but restart failed',
434
+ upgraded: false,
435
+ error: message,
436
+ errorCode,
437
+ targetVersion: target,
438
+ operation: upgradeOperation.getState(),
439
+ contract: upgradeError?.contract || null,
222
440
  });
223
441
  }
224
-
225
- return res.json({ success: true, restarted: true, version: target, pinned: true });
226
442
  } catch (error) {
227
443
  console.error('Failed to upgrade OpenCode:', error);
228
444
  return res.status(500).json({
@@ -234,42 +450,35 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
234
450
 
235
451
  app.get('/api/opencode/upgrade-status', async (_req, res) => {
236
452
  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() },
453
+ const status = await buildUpgradeStatus();
454
+ return res.json(status);
455
+ } catch (error) {
456
+ return res.status(500).json({
457
+ available: null,
458
+ error: error instanceof Error ? error.message : 'Failed to check OpenCode upgrade status',
459
+ operation: upgradeOperation.getState(),
250
460
  });
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;
461
+ }
462
+ });
463
+
464
+ app.get('/api/opencode/contract', async (_req, res) => {
465
+ try {
466
+ const contract = await buildLiveContract();
467
+ const ownership = await resolveOwnershipContext();
264
468
  return res.json({
265
- available,
266
- currentVersion,
267
- latestVersion,
469
+ ...contract,
470
+ ownership: ownership.ownership,
471
+ supplySource: ownership.supplySource,
472
+ canManageUpgrade: ownership.canUpgradeInApp,
473
+ management: ownership.management,
474
+ guidance: ownership.guidance,
268
475
  });
269
476
  } catch (error) {
270
477
  return res.status(500).json({
271
- available: null,
272
- error: error instanceof Error ? error.message : 'Failed to check OpenCode upgrade status',
478
+ schemaVersion: 1,
479
+ phase: 'unknown',
480
+ executionAllowed: false,
481
+ error: error instanceof Error ? error.message : 'Failed to evaluate OpenCode runtime contract',
273
482
  });
274
483
  }
275
484
  });
@@ -283,22 +492,45 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
283
492
  });
284
493
  const health = await healthResponse.json().catch(() => null);
285
494
  if (!healthResponse.ok) {
495
+ const contract = evaluateRuntimeContract({
496
+ reachable: false,
497
+ authenticated: healthResponse.status === 401 || healthResponse.status === 403 ? false : null,
498
+ healthOk: false,
499
+ });
286
500
  return res.status(healthResponse.status).json({
287
501
  healthy: false,
288
502
  error: health?.error || healthResponse.statusText || 'OpenCode health check failed',
503
+ contract,
289
504
  });
290
505
  }
291
- return res.json({ healthy: health?.healthy === true });
506
+ // Reuse lifecycle/sidecar gate: official 2.x ServerInfo omits `healthy`.
507
+ const gate = evaluateOpenCodeHealthBody(health);
508
+ const contract = await buildLiveContract().catch(() => evaluateRuntimeContract({
509
+ serveVersion: gate.version,
510
+ reachable: gate.ok,
511
+ authenticated: true,
512
+ healthOk: gate.ok,
513
+ }));
514
+ return res.json({
515
+ healthy: gate.ok,
516
+ version: gate.version,
517
+ // Healthy is reachability/version shape only — not full execution semantics.
518
+ executionAllowed: contract.executionAllowed === true,
519
+ protocolCompatible: contract.protocolCompatible === true,
520
+ contract,
521
+ });
292
522
  } catch (error) {
293
523
  return res.status(503).json({
294
524
  healthy: false,
295
525
  error: error instanceof Error ? error.message : 'OpenCode health check failed',
526
+ contract: evaluateRuntimeContract({ reachable: false, healthOk: false }),
296
527
  });
297
528
  }
298
529
  });
299
530
 
300
531
  app.get('/api/opencode/version', async (_req, res) => {
301
532
  try {
533
+ const contract = await buildLiveContract();
302
534
  const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
303
535
  method: 'GET',
304
536
  headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
@@ -307,11 +539,20 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
307
539
  if (!healthResponse.ok) {
308
540
  return res.status(healthResponse.status).json({
309
541
  version: null,
542
+ serveVersion: null,
543
+ cliVersion: contract.cliVersion,
310
544
  error: health?.error || healthResponse.statusText || 'Failed to read OpenCode version',
545
+ contract,
311
546
  });
312
547
  }
313
548
  const version = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
314
- return res.json({ version });
549
+ return res.json({
550
+ version,
551
+ serveVersion: version,
552
+ cliVersion: contract.cliVersion,
553
+ versionMismatch: Boolean(version && contract.cliVersion && version !== contract.cliVersion),
554
+ contract,
555
+ });
315
556
  } catch (error) {
316
557
  return res.status(500).json({
317
558
  version: null,
@@ -599,13 +840,6 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
599
840
 
600
841
  await fs.promises.writeFile(AGENTS_MD_PATH, content, 'utf8');
601
842
 
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
843
  return res.json({ success: true });
610
844
  } catch (error) {
611
845
  console.error('Failed to write AGENTS.md:', error);