@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
@@ -1,5 +1,25 @@
1
1
  import { createProjectIdFromPath } from '../projects/project-id.js';
2
2
  import { projectBootstrapSettingsResponse } from './settings-helpers.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';
3
23
  import fs from 'fs';
4
24
  import os from 'os';
5
25
  import path from 'path';
@@ -22,8 +42,23 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
22
42
  buildOpenCodeUrl,
23
43
  getOpenCodeAuthHeaders,
24
44
  onSettingsPersisted,
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,
25
57
  } = dependencies;
26
58
 
59
+ const upgradeOperation = createUpgradeOperationState();
60
+ const resolveDataDir = () => openchamberDataDir || resolveOpenChamberDataDir();
61
+
27
62
  let authLibrary = null;
28
63
  const pendingMcpAuthContextByState = new Map();
29
64
  const PENDING_MCP_AUTH_TTL_MS = 30 * 60 * 1000;
@@ -50,16 +85,87 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
50
85
  };
51
86
 
52
87
  const readOpenCodeCurrentVersion = async () => {
53
- const healthResponse = await fetch(buildOpenCodeUrl('/global/health', ''), {
88
+ const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
54
89
  method: 'GET',
55
90
  headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
56
91
  });
57
92
  const health = await healthResponse.json().catch(() => null);
58
93
  if (!healthResponse.ok) {
59
- 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
+ };
100
+ }
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.
60
125
  }
61
- const currentVersion = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
62
- return { ok: true, currentVersion };
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
+ });
63
169
  };
64
170
 
65
171
  const parseVersionForComparison = (value) => {
@@ -148,6 +254,35 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
148
254
  return fallback;
149
255
  };
150
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
+
151
286
  app.get('/api/config/settings', async (req, res) => {
152
287
  try {
153
288
  const settings = await readSettingsFromDiskMigrated();
@@ -183,51 +318,157 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
183
318
 
184
319
  app.post('/api/opencode/upgrade', async (req, res) => {
185
320
  try {
186
- if (await isBundledOpenCodeBinaryActive()) {
187
- 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({
188
325
  success: false,
189
- 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,
190
332
  });
191
333
  }
192
334
 
193
- const target = normalizeOpenCodeUpgradeTarget(req.body?.target);
194
- if (!target) {
335
+ const rawTarget = typeof req.body?.target === 'string' && req.body.target.trim().length > 0
336
+ ? req.body.target.trim()
337
+ : undefined;
338
+ if (isOpenCode1xVersion(rawTarget)) {
195
339
  return res.status(400).json({
196
340
  success: false,
197
- error: 'OpenCode upgrade requires a semantic version target',
341
+ error: 'OpenCode upgrade refuses 1.x targets. Only pinned opencode2 is allowed.',
342
+ errorCode: 'OPENCODE_UPGRADE_1X_REFUSED',
198
343
  });
199
344
  }
200
-
201
- const response = await fetch(buildOpenCodeUrl('/global/upgrade', ''), {
202
- method: 'POST',
203
- headers: {
204
- 'Content-Type': 'application/json',
205
- Accept: 'application/json',
206
- ...getOpenCodeAuthHeaders(),
207
- },
208
- body: JSON.stringify({ target }),
209
- });
210
- const payload = await response.json().catch(() => null);
211
- if (!response.ok) {
212
- return res.status(response.status).json({
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({
213
350
  success: false,
214
- error: readOpenCodeUpgradeError(payload, response.statusText || 'Failed to upgrade OpenCode'),
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,
215
355
  });
216
356
  }
217
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();
367
+
218
368
  try {
219
- await refreshOpenCodeAfterConfigChange('OpenCode upgrade');
220
- } 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.
221
462
  return res.status(500).json({
222
463
  success: false,
223
- upgraded: true,
224
- error: restartError instanceof Error
225
- ? `OpenCode upgraded, but restart failed: ${restartError.message}`
226
- : '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,
227
470
  });
228
471
  }
229
-
230
- return res.json({ ...(payload ?? { success: true }), restarted: true });
231
472
  } catch (error) {
232
473
  console.error('Failed to upgrade OpenCode:', error);
233
474
  return res.status(500).json({
@@ -239,74 +480,88 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
239
480
 
240
481
  app.get('/api/opencode/upgrade-status', async (_req, res) => {
241
482
  try {
242
- if (await isBundledOpenCodeBinaryActive()) {
243
- const current = await readOpenCodeCurrentVersion().catch(() => ({ ok: false, currentVersion: null }));
244
- return res.json({
245
- available: false,
246
- currentVersion: current.ok ? current.currentVersion : null,
247
- latestVersion: null,
248
- source: 'bundled',
249
- });
250
- }
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(),
490
+ });
491
+ }
492
+ });
251
493
 
252
- const [healthResponse, latestVersion] = await Promise.all([
253
- fetch(buildOpenCodeUrl('/global/health', ''), {
254
- method: 'GET',
255
- headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
256
- }),
257
- fetchLatestOpenCodeVersion(),
258
- ]);
259
- const health = await healthResponse.json().catch(() => null);
260
- if (!healthResponse.ok) {
261
- return res.status(healthResponse.status).json({
262
- available: null,
263
- error: health?.error || healthResponse.statusText || 'Failed to read OpenCode version',
264
- });
265
- }
266
- const currentVersion = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
267
- if (!currentVersion || !latestVersion) {
268
- return res.json({ available: null, currentVersion, latestVersion: latestVersion || null });
269
- }
270
- const available = compareVersions(latestVersion, currentVersion) > 0;
494
+ app.get('/api/opencode/contract', async (_req, res) => {
495
+ try {
496
+ const contract = await buildLiveContract();
497
+ const ownership = await resolveOwnershipContext();
271
498
  return res.json({
272
- available,
273
- currentVersion,
274
- latestVersion,
499
+ ...contract,
500
+ ownership: ownership.ownership,
501
+ supplySource: ownership.supplySource,
502
+ canManageUpgrade: ownership.canUpgradeInApp,
503
+ management: ownership.management,
504
+ guidance: ownership.guidance,
275
505
  });
276
506
  } catch (error) {
277
507
  return res.status(500).json({
278
- available: null,
279
- 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',
280
512
  });
281
513
  }
282
514
  });
283
515
 
284
516
  app.get('/api/opencode/health', async (_req, res) => {
285
517
  try {
286
- const healthResponse = await fetch(buildOpenCodeUrl('/global/health', ''), {
518
+ const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
287
519
  method: 'GET',
288
520
  headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
289
521
  signal: AbortSignal.timeout(4_000),
290
522
  });
291
523
  const health = await healthResponse.json().catch(() => null);
292
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
+ });
293
530
  return res.status(healthResponse.status).json({
294
531
  healthy: false,
295
532
  error: health?.error || healthResponse.statusText || 'OpenCode health check failed',
533
+ contract,
296
534
  });
297
535
  }
298
- 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
+ });
299
552
  } catch (error) {
300
553
  return res.status(503).json({
301
554
  healthy: false,
302
555
  error: error instanceof Error ? error.message : 'OpenCode health check failed',
556
+ contract: evaluateRuntimeContract({ reachable: false, healthOk: false }),
303
557
  });
304
558
  }
305
559
  });
306
560
 
307
561
  app.get('/api/opencode/version', async (_req, res) => {
308
562
  try {
309
- const healthResponse = await fetch(buildOpenCodeUrl('/global/health', ''), {
563
+ const contract = await buildLiveContract();
564
+ const healthResponse = await fetch(buildOpenCodeUrl('/api/info', ''), {
310
565
  method: 'GET',
311
566
  headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
312
567
  });
@@ -314,11 +569,20 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
314
569
  if (!healthResponse.ok) {
315
570
  return res.status(healthResponse.status).json({
316
571
  version: null,
572
+ serveVersion: null,
573
+ cliVersion: contract.cliVersion,
317
574
  error: health?.error || healthResponse.statusText || 'Failed to read OpenCode version',
575
+ contract,
318
576
  });
319
577
  }
320
578
  const version = typeof health?.version === 'string' ? health.version.replace(/^v/, '') : null;
321
- 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
+ });
322
586
  } catch (error) {
323
587
  return res.status(500).json({
324
588
  version: null,
@@ -606,13 +870,6 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
606
870
 
607
871
  await fs.promises.writeFile(AGENTS_MD_PATH, content, 'utf8');
608
872
 
609
- // Refresh OpenCode so it picks up the new AGENTS.md without a full restart
610
- try {
611
- await refreshOpenCodeAfterConfigChange('global behavior (AGENTS.md) updated');
612
- } catch {
613
- // Non-fatal: file was written successfully
614
- }
615
-
616
873
  return res.json({ success: true });
617
874
  } catch (error) {
618
875
  console.error('Failed to write AGENTS.md:', error);