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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/README.md +13 -7
  2. package/bin/cli-opencode.test.js +208 -0
  3. package/bin/cli.js +62 -9
  4. package/bin/lib/cli-startup.js +105 -2
  5. package/bin/lib/cli-startup.test.js +179 -0
  6. package/dist/assets/{AgentAvatar-CRaBJXKs.js → AgentAvatar-CzTtVMnI.js} +1 -1
  7. package/dist/assets/AssistantView-Cg1qQvVe.js +3 -0
  8. package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-hOi72Wdx.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-DCqrQkkz.js} +1 -1
  10. package/dist/assets/Icon-B8fSTLdP.js +1 -0
  11. package/dist/assets/MarkdownRenderer-Cw74oVxV.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-YM7iRmzK.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-BDy_oGJQ.js +3 -0
  14. package/dist/assets/MobileDetailNavigation-iX_hb_Xt.js +1 -0
  15. package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-CAMUupwR.js} +2 -2
  16. package/dist/assets/MobileSurface-CHbdPcLH.js +2 -0
  17. package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-Ci1ODFFX.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-O4cGdz5p.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-CRfGtcmG.js +2 -0
  20. package/dist/assets/SettingsView-Dg8BtTV1.js +25 -0
  21. package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-B8d8qIQ7.js} +1 -1
  22. package/dist/assets/TerminalView-CMiw3s_c.js +8 -0
  23. package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-ByaQYRTp.js} +2 -2
  24. package/dist/assets/appThemeRegistry-BGGHio8T.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-CI7Q-Zw_.js → button-DIIrsMjW.js} +1 -1
  27. package/dist/assets/{collapsible-D5Phaykh.js → collapsible-CnvUeYst.js} +1 -1
  28. package/dist/assets/{context-menu-CubSDW2g.js → context-menu-BGBUpZEQ.js} +1 -1
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-JGFqhlaM.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-CHEwrI93.js} +1 -1
  31. package/dist/assets/{diagram-DdLO8HZ9.js → diagram-rvn7_qBM.js} +1 -1
  32. package/dist/assets/{dialog-B6pnIYmp.js → dialog-CQ63sc9E.js} +1 -1
  33. package/dist/assets/es-Dsmc0A_3.js +15 -0
  34. package/dist/assets/fileMentionTouchSelection-BzzcWJCi.js +3 -0
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-DN6gbLhM.js} +1 -1
  36. package/dist/assets/fr-C_ZlZDI2.js +13 -0
  37. package/dist/assets/gitApi-BF8vuoZc.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-BMztRu0c.js} +1 -1
  39. package/dist/assets/{input-D-__fwRi.js → input-BVu-iykl.js} +1 -1
  40. package/dist/assets/insertionBoundaries-EcAfGT2D.js +1 -0
  41. package/dist/assets/ja-BqqTaWTi.js +15 -0
  42. package/dist/assets/ko-DP2XPM37.js +15 -0
  43. package/dist/assets/main-B7t9lavz.js +2 -0
  44. package/dist/assets/main-D2BzpnUt.css +1 -0
  45. package/dist/assets/main-DWP-prNB.js +169 -0
  46. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  47. package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-D9-B1wdY.js} +1 -1
  48. package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
  49. package/dist/assets/mcpQueries-CfXrAkuU.js +1 -0
  50. package/dist/assets/miniChat-DgOrEQbN.js +2 -0
  51. package/dist/assets/mobile-Co_ZOHkZ.js +2 -0
  52. package/dist/assets/multirun-DelyUO6k.js +4 -0
  53. package/dist/assets/persistence-D0y7226-.js +3 -0
  54. package/dist/assets/pl-BCf5uLlK.js +15 -0
  55. package/dist/assets/platform-CurqO28D.js +6 -0
  56. package/dist/assets/{projectMeta-S1hJ7oBp.js → projectMeta-CPElzJ0n.js} +2 -2
  57. package/dist/assets/pt-BR-W09Q2pvn.js +15 -0
  58. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  59. package/dist/assets/renderElectronMiniChatApp-pOE93w86.js +2 -0
  60. package/dist/assets/renderMobileApp-F7ZSLcw8.js +28 -0
  61. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  62. package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-DSS0KxG3.js} +1 -1
  63. package/dist/assets/runtimeEndpointReset-x2Wgx57R.js +149 -0
  64. package/dist/assets/sessionChangeSummary-BUGSYcHp.js +2 -0
  65. package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-Bss8M20i.js} +1 -1
  66. package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-CE7W7lJ9.js} +1 -1
  67. package/dist/assets/src-lS1keDVV.css +2 -0
  68. package/dist/assets/store-jaP79mqI.js +15 -0
  69. package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-mDR2jXTo.js} +1 -1
  70. package/dist/assets/textarea-XD2oX_Ee.js +1 -0
  71. package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-DYeQ_Hvy.js} +1 -1
  72. package/dist/assets/uk-BgFzz8m2.js +15 -0
  73. package/dist/assets/{url-_z7evzdK.js → url-CGbJbiJq.js} +1 -1
  74. package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-BAWHZ-t0.js} +64 -45
  75. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
  76. package/dist/assets/useDesktopWindowControlsLayout-hN_nLb7T.js +1 -0
  77. package/dist/assets/useEffectiveDirectory-Do9DQ453.js +1 -0
  78. package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-Bjx8zkGM.js} +1 -1
  79. package/dist/assets/useMobileNavigationStore-BsY_gi_K.js +414 -0
  80. package/dist/assets/useSessionAutoCleanup-C4g5KmDf.js +2 -0
  81. package/dist/assets/useUIStore-syVmbuDA.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-B5QXxBok.js} +2 -2
  83. package/dist/assets/vendor-opencode-client-C12gfjag.js +12 -0
  84. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
  85. package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
  86. package/dist/assets/zh-CN-BhqSLgUY.js +15 -0
  87. package/dist/assets/zh-TW-BsaBgY-p.js +15 -0
  88. package/dist/index.html +12 -12
  89. package/dist/mini-chat.html +12 -12
  90. package/dist/mobile.html +12 -12
  91. package/package.json +3 -2
  92. package/server/index.js +423 -20
  93. package/server/lib/assistants/DOCUMENTATION.md +26 -21
  94. package/server/lib/assistants/assign.js +2 -1
  95. package/server/lib/assistants/contact-store.js +37 -19
  96. package/server/lib/assistants/contracts.js +11 -1
  97. package/server/lib/assistants/projection-history.test.js +454 -0
  98. package/server/lib/assistants/routes.js +11 -5
  99. package/server/lib/assistants/service.js +850 -294
  100. package/server/lib/assistants/service.test.js +588 -274
  101. package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
  102. package/server/lib/browser-provider/contract.js +312 -0
  103. package/server/lib/browser-provider/host.js +421 -0
  104. package/server/lib/browser-provider/host.test.js +304 -0
  105. package/server/lib/browser-provider/routes.js +122 -0
  106. package/server/lib/browser-provider/routes.test.js +120 -0
  107. package/server/lib/browser-provider/selection-store.js +43 -0
  108. package/server/lib/browser-provider/selection-store.test.js +35 -0
  109. package/server/lib/conversations/DOCUMENTATION.md +5 -5
  110. package/server/lib/conversations/service.js +87 -57
  111. package/server/lib/conversations/service.test.js +132 -100
  112. package/server/lib/conversations/validation.js +11 -1
  113. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  114. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  115. package/server/lib/event-stream/global-hub.js +51 -5
  116. package/server/lib/event-stream/reasoning-projection.js +16 -1
  117. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  118. package/server/lib/event-stream/runtime.js +57 -20
  119. package/server/lib/event-stream/runtime.test.js +112 -0
  120. package/server/lib/llm/DOCUMENTATION.md +95 -84
  121. package/server/lib/llm/catalog.js +111 -45
  122. package/server/lib/llm/catalog.test.js +121 -59
  123. package/server/lib/llm/completions.js +41 -12
  124. package/server/lib/llm/completions.test.js +45 -4
  125. package/server/lib/llm/generate.js +516 -453
  126. package/server/lib/llm/generate.test.js +705 -484
  127. package/server/lib/llm/routes.js +8 -4
  128. package/server/lib/message-queue/DOCUMENTATION.md +5 -5
  129. package/server/lib/message-queue/opencode-adapter.js +191 -104
  130. package/server/lib/message-queue/opencode-adapter.test.js +328 -58
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/message-queue/runtime.test.js +6 -6
  133. package/server/lib/message-queue/worker.js +7 -1
  134. package/server/lib/notifications/DOCUMENTATION.md +3 -1
  135. package/server/lib/notifications/runtime.js +133 -31
  136. package/server/lib/notifications/runtime.test.js +103 -0
  137. package/server/lib/notifications/template-runtime.js +130 -38
  138. package/server/lib/notifications/template-runtime.test.js +96 -0
  139. package/server/lib/opencode/DOCUMENTATION.md +111 -16
  140. package/server/lib/opencode/agent-document.d.ts +28 -0
  141. package/server/lib/opencode/agent-document.js +280 -0
  142. package/server/lib/opencode/agent-document.test.js +137 -0
  143. package/server/lib/opencode/agents.js +96 -0
  144. package/server/lib/opencode/auth-state-runtime.js +5 -0
  145. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  146. package/server/lib/opencode/config-entity-routes.js +117 -103
  147. package/server/lib/opencode/config-entity-routes.test.js +148 -10
  148. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  149. package/server/lib/opencode/ensure-cli.d.ts +8 -0
  150. package/server/lib/opencode/ensure-cli.js +238 -0
  151. package/server/lib/opencode/ensure-cli.test.js +119 -0
  152. package/server/lib/opencode/env-runtime.js +208 -111
  153. package/server/lib/opencode/env-runtime.test.js +211 -14
  154. package/server/lib/opencode/feature-routes-runtime.js +90 -5
  155. package/server/lib/opencode/feature-routes-runtime.test.js +25 -0
  156. package/server/lib/opencode/http-server-connections.js +98 -0
  157. package/server/lib/opencode/http-server-connections.test.js +321 -0
  158. package/server/lib/opencode/lifecycle.js +539 -95
  159. package/server/lib/opencode/lifecycle.test.js +698 -6
  160. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  161. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  162. package/server/lib/opencode/network-runtime.js +28 -16
  163. package/server/lib/opencode/network-runtime.test.js +68 -4
  164. package/server/lib/opencode/openchamber-routes.js +7 -1
  165. package/server/lib/opencode/opencode2-pin.js +139 -0
  166. package/server/lib/opencode/opencode2-pin.test.js +88 -0
  167. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
  168. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  169. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  170. package/server/lib/opencode/plugin-compatibility.js +15 -0
  171. package/server/lib/opencode/plugin-compatibility.test.js +21 -0
  172. package/server/lib/opencode/plugin-routes.js +3 -18
  173. package/server/lib/opencode/plugin-routes.test.js +9 -9
  174. package/server/lib/opencode/plugins.js +4 -3
  175. package/server/lib/opencode/plugins.test.js +12 -0
  176. package/server/lib/opencode/provider-catalog.js +58 -12
  177. package/server/lib/opencode/provider-catalog.test.js +48 -0
  178. package/server/lib/opencode/proxy.js +373 -28
  179. package/server/lib/opencode/proxy.test.js +353 -0
  180. package/server/lib/opencode/routes.js +333 -76
  181. package/server/lib/opencode/routes.test.js +315 -50
  182. package/server/lib/opencode/runtime-contract.js +401 -0
  183. package/server/lib/opencode/runtime-contract.test.js +214 -0
  184. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  185. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  186. package/server/lib/opencode/server-utils-runtime.js +35 -0
  187. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  188. package/server/lib/opencode/shared-service.js +147 -0
  189. package/server/lib/opencode/shared-service.test.js +93 -0
  190. package/server/lib/opencode/shared.js +3 -3
  191. package/server/lib/opencode/shutdown-runtime.js +15 -6
  192. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  193. package/server/lib/opencode/skill-routes.js +26 -40
  194. package/server/lib/opencode/skill-routes.test.js +71 -7
  195. package/server/lib/opencode/skills.test.js +15 -0
  196. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  197. package/server/lib/opencode/static-routes-runtime.js +3 -1
  198. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  199. package/server/lib/opencode/upgrade-screen.d.ts +30 -0
  200. package/server/lib/opencode/upgrade-screen.js +299 -0
  201. package/server/lib/opencode/upgrade-screen.test.js +196 -0
  202. package/server/lib/opencode/v1-migration-gate.js +157 -0
  203. package/server/lib/opencode/v1-migration-gate.test.js +111 -0
  204. package/server/lib/opencode/v2-client.js +60 -0
  205. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  206. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  207. package/server/lib/opencode/watcher.js +2 -1
  208. package/server/lib/opencode/watcher.test.js +1 -1
  209. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  210. package/server/lib/permission-auto-accept/runtime.js +14 -7
  211. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  212. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  213. package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
  214. package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
  215. package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
  216. package/server/lib/scheduled-tasks/routes.js +27 -0
  217. package/server/lib/scheduled-tasks/routes.test.js +94 -0
  218. package/server/lib/scheduled-tasks/runtime.js +381 -310
  219. package/server/lib/scheduled-tasks/runtime.test.js +600 -309
  220. package/server/lib/session-goal/DOCUMENTATION.md +173 -22
  221. package/server/lib/session-goal/capability.js +57 -0
  222. package/server/lib/session-goal/capability.test.js +33 -0
  223. package/server/lib/session-goal/routes.js +240 -2
  224. package/server/lib/session-goal/routes.test.js +211 -0
  225. package/server/lib/session-goal/runtime.js +1617 -113
  226. package/server/lib/session-goal/runtime.test.js +1500 -70
  227. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  228. package/server/lib/session-index/event-ingest.js +89 -16
  229. package/server/lib/session-index/event-ingest.test.js +73 -0
  230. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  231. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  232. package/server/lib/session-index/routes.js +63 -10
  233. package/server/lib/session-index/routes.test.js +8 -8
  234. package/server/lib/session-index/service.js +7 -1
  235. package/server/lib/session-index/sync-runtime.js +171 -41
  236. package/server/lib/session-index/sync-runtime.test.js +164 -9
  237. package/server/lib/session-metadata/DOCUMENTATION.md +96 -0
  238. package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
  239. package/server/lib/session-metadata/opencode-session-record.js +316 -0
  240. package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
  241. package/server/lib/session-metadata/routes.js +197 -0
  242. package/server/lib/session-metadata/routes.test.js +156 -0
  243. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  244. package/server/lib/session-metadata/session-archive.js +654 -0
  245. package/server/lib/session-metadata/session-archive.test.js +359 -0
  246. package/server/lib/session-metadata/session-metadata-store.d.ts +38 -0
  247. package/server/lib/session-metadata/session-metadata-store.js +422 -0
  248. package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
  249. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  250. package/server/lib/session-metadata/session-projection.js +279 -0
  251. package/server/lib/session-metadata/session-projection.test.js +64 -0
  252. package/server/lib/session-metadata/system-session.js +55 -0
  253. package/server/lib/session-metadata/system-session.test.js +55 -0
  254. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  255. package/server/lib/session-title/native-runtime.test.js +167 -0
  256. package/server/lib/session-title/runtime.js +69 -44
  257. package/server/lib/session-title/session-access.js +46 -0
  258. package/server/lib/session-title/session-access.test.js +91 -0
  259. package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
  260. package/server/lib/session-turn-pages/reconcile.service.js +1 -1
  261. package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
  262. package/server/lib/session-turn-pages/routes.js +123 -75
  263. package/server/lib/session-turn-pages/routes.test.js +566 -0
  264. package/server/lib/session-turn-pages/service.js +6 -2
  265. package/server/lib/session-turn-pages/service.test.js +34 -0
  266. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  267. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  268. package/server/lib/small-model/DOCUMENTATION.md +101 -146
  269. package/server/lib/small-model/catalog.js +84 -54
  270. package/server/lib/small-model/catalog.test.js +111 -9
  271. package/server/lib/small-model/custom-api.js +68 -0
  272. package/server/lib/small-model/custom-api.test.js +64 -0
  273. package/server/lib/small-model/index.js +125 -241
  274. package/server/lib/small-model/resolve.js +35 -120
  275. package/server/lib/small-model/resolve.test.js +20 -110
  276. package/server/lib/small-model/routes.js +1 -3
  277. package/server/lib/small-model/summary-settings.test.js +157 -253
  278. package/server/opencode-proxy.test.js +63 -12
  279. package/dist/assets/AssistantView-BUveAgc4.js +0 -3
  280. package/dist/assets/Icon-BgdfPhNs.js +0 -1
  281. package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
  282. package/dist/assets/MarkstreamRendererImpl-D2dsW5Lg.js +0 -3
  283. package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
  284. package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
  285. package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
  286. package/dist/assets/SettingsView-PkXIavr7.js +0 -25
  287. package/dist/assets/TerminalView-CDdzF4yi.js +0 -8
  288. package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
  289. package/dist/assets/es-DA3MqOo0.js +0 -15
  290. package/dist/assets/fileMentionTouchSelection-DNeiqns-.js +0 -4
  291. package/dist/assets/fr-Dbz0CdIA.js +0 -13
  292. package/dist/assets/gitApi-DXyC4MiV.js +0 -7
  293. package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
  294. package/dist/assets/ja-DumanVQA.js +0 -15
  295. package/dist/assets/ko-C0IkiUjQ.js +0 -15
  296. package/dist/assets/main-BPS5ylib.js +0 -2
  297. package/dist/assets/main-Cpd9VWYs.js +0 -169
  298. package/dist/assets/main-qSeeJRW1.css +0 -1
  299. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  300. package/dist/assets/markdownTheme-DOfGg-A7.js +0 -1
  301. package/dist/assets/miniChat-B8_KLWiY.js +0 -2
  302. package/dist/assets/mobile-CtktKrXn.js +0 -2
  303. package/dist/assets/multirun-CYfP9Cfm.js +0 -4
  304. package/dist/assets/persistence-Ce3AnpEr.js +0 -3
  305. package/dist/assets/pl-B9No6MQp.js +0 -15
  306. package/dist/assets/platform-0w_Q9_ji.js +0 -2
  307. package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
  308. package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
  309. package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
  310. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  311. package/dist/assets/runtimeEndpointReset-ChOxt-J7.js +0 -147
  312. package/dist/assets/sessionChangeSummary-NnxcqCTI.js +0 -2
  313. package/dist/assets/src-rS3zCcsC.css +0 -2
  314. package/dist/assets/store-5B_LDnLM.js +0 -15
  315. package/dist/assets/textarea-bHYxZtfH.js +0 -1
  316. package/dist/assets/uk-C_1KkqyP.js +0 -15
  317. package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
  318. package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
  319. package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
  320. package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
  321. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  322. package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
  323. package/dist/assets/zh-CN-uWILgBdt.js +0 -15
  324. package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
  325. package/server/lib/small-model/call.js +0 -427
  326. package/server/lib/small-model/call.test.js +0 -382
  327. package/server/lib/small-model/opencode-session.js +0 -339
  328. package/server/lib/small-model/opencode-session.test.js +0 -177
@@ -0,0 +1,431 @@
1
+ /**
2
+ * Ownership + upgrade status for OpenCode runtimes (ticket 12).
3
+ *
4
+ * Binary ownership and process ownership are modeled separately:
5
+ * - arbitrary external serve is never upgraded/restarted in-app
6
+ * - official shared service can be replaced using an owned-cache binary
7
+ * - managed process using a global CLI is "global-cli" (manual guidance)
8
+ * - managed process using OpenChamber cache under opencode-cli/ is "owned-cache"
9
+ * - bundled desktop binary cannot be upgraded separately
10
+ *
11
+ * In-app upgrade only mutates the owned cache, then launches that target and
12
+ * verifies the running serve identity/version/contract before success.
13
+ */
14
+
15
+ import path from 'node:path';
16
+ import {
17
+ PINNED_OPENCODE2_VERSION,
18
+ compareOpenCode2Versions,
19
+ isOpenCode1xVersion,
20
+ resolveOpenCode2UpgradeTarget,
21
+ } from './opencode2-pin.js';
22
+ import {
23
+ installedOpenCode2BinaryPath,
24
+ resolveOpenChamberDataDir,
25
+ } from './ensure-cli.js';
26
+ import { evaluateRuntimeContract, normalizeRuntimeVersion } from './runtime-contract.js';
27
+
28
+ /** @typedef {'shared-service' | 'external-serve' | 'owned-cache' | 'global-cli' | 'bundled' | 'settings' | 'env' | 'unknown'} RuntimeOwnership */
29
+
30
+ /**
31
+ * @param {string | null | undefined} binaryPath
32
+ * @param {string | null | undefined} dataDir
33
+ */
34
+ export function isOwnedOpenCodeCachePath(binaryPath, dataDir) {
35
+ if (typeof binaryPath !== 'string' || !binaryPath.trim()) return false;
36
+ const root = typeof dataDir === 'string' && dataDir.trim()
37
+ ? path.resolve(dataDir.trim())
38
+ : resolveOpenChamberDataDir();
39
+ const cacheRoot = path.resolve(root, 'opencode-cli');
40
+ const resolved = path.resolve(binaryPath.trim());
41
+ const rel = path.relative(cacheRoot, resolved);
42
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
43
+ }
44
+
45
+ /**
46
+ * @param {{
47
+ * isExternal?: boolean,
48
+ * isSharedService?: boolean,
49
+ * binarySource?: string | null,
50
+ * binaryPath?: string | null,
51
+ * dataDir?: string | null,
52
+ * }} input
53
+ */
54
+ export function classifyRuntimeOwnership(input = {}) {
55
+ if (input.isSharedService === true) {
56
+ return {
57
+ ownership: /** @type {RuntimeOwnership} */ ('shared-service'),
58
+ processOwnership: 'shared',
59
+ binaryOwnership: isOwnedOpenCodeCachePath(input.binaryPath, input.dataDir) ? 'owned-cache' : 'external-or-global',
60
+ canUpgradeInApp: true,
61
+ supplySource: 'shared-service',
62
+ management: 'in-app',
63
+ reason: null,
64
+ guidance: null,
65
+ };
66
+ }
67
+ if (input.isExternal === true) {
68
+ return {
69
+ ownership: /** @type {RuntimeOwnership} */ ('external-serve'),
70
+ processOwnership: 'external',
71
+ binaryOwnership: isOwnedOpenCodeCachePath(input.binaryPath, input.dataDir) ? 'owned-cache' : 'external-or-global',
72
+ canUpgradeInApp: false,
73
+ supplySource: 'external-serve',
74
+ management: 'manual-external',
75
+ reason: 'external-serve',
76
+ guidance: 'This is an external OpenCode serve. Restart or upgrade it yourself; OpenChamber will not replace that process.',
77
+ };
78
+ }
79
+
80
+ const source = typeof input.binarySource === 'string' ? input.binarySource.trim() : '';
81
+ const ownedPath = isOwnedOpenCodeCachePath(input.binaryPath, input.dataDir);
82
+
83
+ if (source === 'bundled') {
84
+ return {
85
+ ownership: /** @type {RuntimeOwnership} */ ('bundled'),
86
+ processOwnership: 'managed',
87
+ binaryOwnership: 'bundled',
88
+ canUpgradeInApp: false,
89
+ supplySource: 'bundled',
90
+ management: 'bundled',
91
+ reason: 'bundled',
92
+ guidance: 'OpenCode is bundled with OpenChamber Desktop and cannot be upgraded separately from the app.',
93
+ };
94
+ }
95
+
96
+ if (source === 'installed' || ownedPath) {
97
+ return {
98
+ ownership: /** @type {RuntimeOwnership} */ ('owned-cache'),
99
+ processOwnership: 'managed',
100
+ binaryOwnership: 'owned-cache',
101
+ canUpgradeInApp: true,
102
+ supplySource: 'owned-cache',
103
+ management: 'in-app',
104
+ reason: null,
105
+ guidance: null,
106
+ };
107
+ }
108
+
109
+ if (source === 'settings') {
110
+ return {
111
+ ownership: /** @type {RuntimeOwnership} */ ('settings'),
112
+ processOwnership: 'managed',
113
+ binaryOwnership: 'user-configured',
114
+ canUpgradeInApp: false,
115
+ supplySource: 'settings',
116
+ management: 'manual-global',
117
+ reason: 'settings-binary',
118
+ guidance: 'OpenCode is launched from a user-configured binary path. Change or upgrade that binary yourself, or clear the setting to use OpenChamber-managed cache.',
119
+ };
120
+ }
121
+
122
+ if (source === 'env') {
123
+ return {
124
+ ownership: /** @type {RuntimeOwnership} */ ('env'),
125
+ processOwnership: 'managed',
126
+ binaryOwnership: 'env',
127
+ canUpgradeInApp: false,
128
+ supplySource: 'env',
129
+ management: 'manual-global',
130
+ reason: 'env-binary',
131
+ guidance: 'OpenCode is launched from OPENCODE_BINARY / related env. Upgrade that install yourself, or unset the env to allow OpenChamber-owned cache management.',
132
+ };
133
+ }
134
+
135
+ // path / fallback / where / discovered → treat as global CLI (read-only for upgrade).
136
+ if (source === 'path' || source === 'fallback' || source === 'where' || source === 'discovered') {
137
+ return {
138
+ ownership: /** @type {RuntimeOwnership} */ ('global-cli'),
139
+ processOwnership: 'managed',
140
+ binaryOwnership: 'global-cli',
141
+ canUpgradeInApp: false,
142
+ supplySource: 'global-cli',
143
+ management: 'manual-global',
144
+ reason: 'global-cli',
145
+ guidance: 'OpenChamber is using your global OpenCode CLI. Upgrade it with your package manager (for example npm/bun), or install an OpenChamber-owned cache and select that target explicitly.',
146
+ };
147
+ }
148
+
149
+ return {
150
+ ownership: /** @type {RuntimeOwnership} */ ('unknown'),
151
+ processOwnership: input.isExternal ? 'external' : 'managed',
152
+ binaryOwnership: ownedPath ? 'owned-cache' : 'unknown',
153
+ canUpgradeInApp: ownedPath,
154
+ supplySource: ownedPath ? 'owned-cache' : 'unknown',
155
+ management: ownedPath ? 'in-app' : 'none',
156
+ reason: ownedPath ? null : 'unknown-ownership',
157
+ guidance: ownedPath
158
+ ? null
159
+ : 'OpenCode supply source could not be classified. In-app upgrade is disabled until ownership is clear.',
160
+ };
161
+ }
162
+
163
+ /**
164
+ * @param {{
165
+ * ownership: ReturnType<typeof classifyRuntimeOwnership>,
166
+ * serveVersion?: string | null,
167
+ * cliVersion?: string | null,
168
+ * targetVersion?: string | null,
169
+ * contract?: ReturnType<typeof evaluateRuntimeContract> | null,
170
+ * operation?: Record<string, unknown> | null,
171
+ * hasActiveTasks?: boolean,
172
+ * }} input
173
+ */
174
+ export function buildUpgradeStatusSnapshot(input) {
175
+ const ownership = input.ownership;
176
+ const serveVersion = normalizeRuntimeVersion(input.serveVersion);
177
+ const cliVersion = normalizeRuntimeVersion(input.cliVersion);
178
+ const currentVersion = serveVersion || cliVersion;
179
+ let targetVersion = null;
180
+ try {
181
+ targetVersion = resolveOpenCode2UpgradeTarget(input.targetVersion ?? PINNED_OPENCODE2_VERSION);
182
+ } catch {
183
+ targetVersion = PINNED_OPENCODE2_VERSION;
184
+ }
185
+ if (isOpenCode1xVersion(targetVersion)) {
186
+ targetVersion = PINNED_OPENCODE2_VERSION;
187
+ }
188
+
189
+ const contract = input.contract || evaluateRuntimeContract({
190
+ serveVersion,
191
+ cliVersion,
192
+ reachable: serveVersion != null || cliVersion != null,
193
+ authenticated: true,
194
+ healthOk: serveVersion != null,
195
+ });
196
+
197
+ const canManage = ownership.canUpgradeInApp === true;
198
+ const newerTarget = Boolean(
199
+ currentVersion
200
+ && targetVersion
201
+ && !isOpenCode1xVersion(currentVersion)
202
+ && compareOpenCode2Versions(targetVersion, currentVersion) > 0,
203
+ );
204
+ const available = canManage && newerTarget ? true : (canManage ? false : null);
205
+
206
+ /** @type {string | null} */
207
+ let reason = ownership.reason;
208
+ if (canManage && available === false && currentVersion && targetVersion) {
209
+ reason = 'up-to-date';
210
+ }
211
+ if (!canManage && ownership.reason) {
212
+ reason = ownership.reason;
213
+ }
214
+ if (input.hasActiveTasks === true && canManage && available) {
215
+ reason = reason || 'active-tasks';
216
+ }
217
+
218
+ return {
219
+ schemaVersion: 1,
220
+ available,
221
+ currentVersion,
222
+ serveVersion,
223
+ cliVersion,
224
+ latestVersion: targetVersion,
225
+ targetVersion,
226
+ supplySource: ownership.supplySource,
227
+ ownership: ownership.ownership,
228
+ processOwnership: ownership.processOwnership,
229
+ binaryOwnership: ownership.binaryOwnership,
230
+ canManage,
231
+ management: ownership.management,
232
+ reason,
233
+ guidance: ownership.guidance,
234
+ hasActiveTasks: input.hasActiveTasks === true,
235
+ contract,
236
+ operation: input.operation && typeof input.operation === 'object'
237
+ ? input.operation
238
+ : { status: 'idle' },
239
+ };
240
+ }
241
+
242
+ /**
243
+ * In-memory upgrade operation state (single-flight).
244
+ * @returns {{
245
+ * getState: () => object,
246
+ * begin: (target: string) => { ok: true } | { ok: false, status: number, body: object },
247
+ * succeed: (result: object) => void,
248
+ * fail: (error: object) => void,
249
+ * resetIdle: () => void,
250
+ * }}
251
+ */
252
+ export function createUpgradeOperationState() {
253
+ /** @type {{
254
+ * status: 'idle' | 'running' | 'succeeded' | 'failed',
255
+ * targetVersion: string | null,
256
+ * startedAt: number | null,
257
+ * finishedAt: number | null,
258
+ * phase: string | null,
259
+ * error: string | null,
260
+ * errorCode: string | null,
261
+ * serveVersion: string | null,
262
+ * binaryPath: string | null,
263
+ * }} */
264
+ let state = {
265
+ status: 'idle',
266
+ targetVersion: null,
267
+ startedAt: null,
268
+ finishedAt: null,
269
+ phase: null,
270
+ error: null,
271
+ errorCode: null,
272
+ serveVersion: null,
273
+ binaryPath: null,
274
+ };
275
+
276
+ return {
277
+ getState: () => ({ ...state }),
278
+ begin: (target) => {
279
+ if (state.status === 'running') {
280
+ return {
281
+ ok: false,
282
+ status: 409,
283
+ body: {
284
+ success: false,
285
+ error: 'An OpenCode upgrade is already in progress',
286
+ errorCode: 'UPGRADE_IN_PROGRESS',
287
+ operation: { ...state },
288
+ },
289
+ };
290
+ }
291
+ state = {
292
+ status: 'running',
293
+ targetVersion: target,
294
+ startedAt: Date.now(),
295
+ finishedAt: null,
296
+ phase: 'starting',
297
+ error: null,
298
+ errorCode: null,
299
+ serveVersion: null,
300
+ binaryPath: null,
301
+ };
302
+ return { ok: true };
303
+ },
304
+ setPhase: (phase, extra = {}) => {
305
+ if (state.status !== 'running') return;
306
+ state = { ...state, phase, ...extra };
307
+ },
308
+ succeed: (result = {}) => {
309
+ state = {
310
+ ...state,
311
+ status: 'succeeded',
312
+ finishedAt: Date.now(),
313
+ phase: 'verified',
314
+ error: null,
315
+ errorCode: null,
316
+ serveVersion: result.serveVersion ?? state.serveVersion,
317
+ binaryPath: result.binaryPath ?? state.binaryPath,
318
+ };
319
+ },
320
+ fail: (error = {}) => {
321
+ state = {
322
+ ...state,
323
+ status: 'failed',
324
+ finishedAt: Date.now(),
325
+ phase: error.phase || state.phase || 'failed',
326
+ error: typeof error.error === 'string' ? error.error : (error.message || 'Upgrade failed'),
327
+ errorCode: error.errorCode || error.code || 'UPGRADE_FAILED',
328
+ serveVersion: error.serveVersion ?? state.serveVersion,
329
+ binaryPath: error.binaryPath ?? state.binaryPath,
330
+ };
331
+ },
332
+ resetIdle: () => {
333
+ state = {
334
+ status: 'idle',
335
+ targetVersion: null,
336
+ startedAt: null,
337
+ finishedAt: null,
338
+ phase: null,
339
+ error: null,
340
+ errorCode: null,
341
+ serveVersion: null,
342
+ binaryPath: null,
343
+ };
344
+ },
345
+ };
346
+ }
347
+
348
+ /**
349
+ * Resolve the on-disk path OpenChamber will use for an owned-cache target.
350
+ * @param {string} targetVersion
351
+ * @param {object} [options]
352
+ */
353
+ export function resolveOwnedCacheBinaryPath(targetVersion, options = {}) {
354
+ const version = resolveOpenCode2UpgradeTarget(targetVersion);
355
+ return installedOpenCode2BinaryPath(version, options);
356
+ }
357
+
358
+ /**
359
+ * Pure post-upgrade verification: running serve must match target and pass contract.
360
+ * @param {{
361
+ * targetVersion: string,
362
+ * serveVersion?: string | null,
363
+ * cliVersion?: string | null,
364
+ * binaryPath?: string | null,
365
+ * expectedBinaryPath?: string | null,
366
+ * contract?: ReturnType<typeof evaluateRuntimeContract> | null,
367
+ * }} input
368
+ */
369
+ export function evaluateOwnedUpgradeResult(input) {
370
+ const target = resolveOpenCode2UpgradeTarget(input.targetVersion);
371
+ const serveVersion = normalizeRuntimeVersion(input.serveVersion);
372
+ const cliVersion = normalizeRuntimeVersion(input.cliVersion);
373
+ const contract = input.contract || evaluateRuntimeContract({
374
+ serveVersion,
375
+ cliVersion,
376
+ reachable: true,
377
+ authenticated: true,
378
+ healthOk: Boolean(serveVersion),
379
+ });
380
+
381
+ if (!serveVersion) {
382
+ return {
383
+ ok: false,
384
+ errorCode: 'UPGRADE_SERVE_VERSION_MISSING',
385
+ error: 'Upgrade started but the running OpenCode serve did not report a version',
386
+ serveVersion,
387
+ targetVersion: target,
388
+ contract,
389
+ };
390
+ }
391
+ if (serveVersion !== target) {
392
+ return {
393
+ ok: false,
394
+ errorCode: 'UPGRADE_SERVE_VERSION_MISMATCH',
395
+ error: `Running serve version ${serveVersion} does not match upgrade target ${target}`,
396
+ serveVersion,
397
+ targetVersion: target,
398
+ contract,
399
+ };
400
+ }
401
+ if (input.expectedBinaryPath && input.binaryPath
402
+ && path.resolve(input.binaryPath) !== path.resolve(input.expectedBinaryPath)) {
403
+ return {
404
+ ok: false,
405
+ errorCode: 'UPGRADE_BINARY_IDENTITY_MISMATCH',
406
+ error: 'Managed process is not using the owned-cache binary selected for upgrade',
407
+ serveVersion,
408
+ targetVersion: target,
409
+ contract,
410
+ };
411
+ }
412
+ if (
413
+ !contract.executionAllowed
414
+ || !contract.protocolCompatible
415
+ ) {
416
+ return {
417
+ ok: false,
418
+ errorCode: 'UPGRADE_CONTRACT_FAILED',
419
+ error: 'Running serve version matches target but failed runtime contract admission',
420
+ serveVersion,
421
+ targetVersion: target,
422
+ contract,
423
+ };
424
+ }
425
+ return {
426
+ ok: true,
427
+ serveVersion,
428
+ targetVersion: target,
429
+ contract,
430
+ };
431
+ }
@@ -0,0 +1,160 @@
1
+ import path from 'node:path';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ buildUpgradeStatusSnapshot,
5
+ classifyRuntimeOwnership,
6
+ createUpgradeOperationState,
7
+ evaluateOwnedUpgradeResult,
8
+ isOwnedOpenCodeCachePath,
9
+ } from './owned-runtime-upgrade.js';
10
+ import { evaluateRuntimeContract } from './runtime-contract.js';
11
+
12
+ describe('owned runtime upgrade (ticket 12)', () => {
13
+ const dataDir = path.join('/tmp', 'openchamber-owned-upgrade-test');
14
+ const ownedBinary = path.join(dataDir, 'opencode-cli', '2.0.12', 'opencode');
15
+
16
+ it('models binary vs process ownership separately', () => {
17
+ expect(isOwnedOpenCodeCachePath(ownedBinary, dataDir)).toBe(true);
18
+ expect(isOwnedOpenCodeCachePath('/usr/local/bin/opencode', dataDir)).toBe(false);
19
+
20
+ const externalUsingOwnedFile = classifyRuntimeOwnership({
21
+ isExternal: true,
22
+ binaryPath: ownedBinary,
23
+ binarySource: 'installed',
24
+ dataDir,
25
+ });
26
+ expect(externalUsingOwnedFile.ownership).toBe('external-serve');
27
+ expect(externalUsingOwnedFile.canUpgradeInApp).toBe(false);
28
+ expect(externalUsingOwnedFile.binaryOwnership).toBe('owned-cache');
29
+
30
+ const managedGlobal = classifyRuntimeOwnership({
31
+ isExternal: false,
32
+ binarySource: 'path',
33
+ binaryPath: '/usr/local/bin/opencode',
34
+ dataDir,
35
+ });
36
+ expect(managedGlobal.ownership).toBe('global-cli');
37
+ expect(managedGlobal.canUpgradeInApp).toBe(false);
38
+ expect(managedGlobal.guidance).toMatch(/global/i);
39
+
40
+ const owned = classifyRuntimeOwnership({
41
+ isExternal: false,
42
+ binarySource: 'installed',
43
+ binaryPath: ownedBinary,
44
+ dataDir,
45
+ });
46
+ expect(owned.ownership).toBe('owned-cache');
47
+ expect(owned.canUpgradeInApp).toBe(true);
48
+
49
+ const bundled = classifyRuntimeOwnership({
50
+ isExternal: false,
51
+ binarySource: 'bundled',
52
+ binaryPath: '/app/resources/opencode-cli/opencode',
53
+ });
54
+ expect(bundled.canUpgradeInApp).toBe(false);
55
+ expect(bundled.management).toBe('bundled');
56
+ });
57
+
58
+ it('upgrade-status exposes manage rights and manual guidance', () => {
59
+ const globalStatus = buildUpgradeStatusSnapshot({
60
+ ownership: classifyRuntimeOwnership({ isExternal: false, binarySource: 'path' }),
61
+ serveVersion: '2.0.12',
62
+ cliVersion: '2.0.12',
63
+ targetVersion: '2.0.14',
64
+ });
65
+ expect(globalStatus.canManage).toBe(false);
66
+ expect(globalStatus.available).toBeNull();
67
+ expect(globalStatus.management).toBe('manual-global');
68
+ expect(globalStatus.guidance).toBeTruthy();
69
+ expect(globalStatus.currentVersion).toBe('2.0.12');
70
+ expect(globalStatus.targetVersion).toBe('2.0.14');
71
+
72
+ const ownedStatus = buildUpgradeStatusSnapshot({
73
+ ownership: classifyRuntimeOwnership({
74
+ isExternal: false,
75
+ binarySource: 'installed',
76
+ binaryPath: ownedBinary,
77
+ dataDir,
78
+ }),
79
+ serveVersion: '2.0.12',
80
+ targetVersion: '2.0.14',
81
+ });
82
+ expect(ownedStatus.canManage).toBe(true);
83
+ expect(ownedStatus.available).toBe(true);
84
+ expect(ownedStatus.management).toBe('in-app');
85
+ });
86
+
87
+ it('verifies running serve identity before upgrade success', () => {
88
+ const contractOk = evaluateRuntimeContract({
89
+ serveVersion: '2.0.14',
90
+ reachable: true,
91
+ authenticated: true,
92
+ healthOk: true,
93
+ migrationAdmitTranscript: true,
94
+ });
95
+ expect(evaluateOwnedUpgradeResult({
96
+ targetVersion: '2.0.14',
97
+ serveVersion: '2.0.14',
98
+ binaryPath: ownedBinary,
99
+ expectedBinaryPath: ownedBinary,
100
+ contract: contractOk,
101
+ }).ok).toBe(true);
102
+
103
+ expect(evaluateOwnedUpgradeResult({
104
+ targetVersion: '2.0.14',
105
+ serveVersion: '2.0.12',
106
+ contract: evaluateRuntimeContract({
107
+ serveVersion: '2.0.12',
108
+ reachable: true,
109
+ authenticated: true,
110
+ healthOk: true,
111
+ migrationAdmitTranscript: true,
112
+ }),
113
+ })).toMatchObject({
114
+ ok: false,
115
+ errorCode: 'UPGRADE_SERVE_VERSION_MISMATCH',
116
+ });
117
+
118
+ expect(evaluateOwnedUpgradeResult({
119
+ targetVersion: '2.0.14',
120
+ serveVersion: '2.0.14',
121
+ binaryPath: '/usr/bin/opencode',
122
+ expectedBinaryPath: ownedBinary,
123
+ contract: contractOk,
124
+ }).errorCode).toBe('UPGRADE_BINARY_IDENTITY_MISMATCH');
125
+
126
+ // Target match alone is not enough — below-minimum versions must not succeed.
127
+ expect(evaluateOwnedUpgradeResult({
128
+ targetVersion: '2.0.5',
129
+ serveVersion: '2.0.5',
130
+ contract: evaluateRuntimeContract({
131
+ serveVersion: '2.0.5',
132
+ reachable: true,
133
+ authenticated: true,
134
+ healthOk: true,
135
+ migrationAdmitTranscript: true,
136
+ }),
137
+ })).toMatchObject({
138
+ ok: false,
139
+ errorCode: 'UPGRADE_CONTRACT_FAILED',
140
+ });
141
+ });
142
+
143
+ it('keeps concurrent upgrade state stable and reconciles failures', () => {
144
+ const op = createUpgradeOperationState();
145
+ expect(op.begin('2.0.14').ok).toBe(true);
146
+ const second = op.begin('2.0.14');
147
+ expect(second.ok).toBe(false);
148
+ expect(second.status).toBe(409);
149
+ expect(second.body.errorCode).toBe('UPGRADE_IN_PROGRESS');
150
+
151
+ op.setPhase('download');
152
+ op.fail({ error: 'download failed', errorCode: 'UPGRADE_DOWNLOAD_FAILED', phase: 'download' });
153
+ expect(op.getState()).toMatchObject({
154
+ status: 'failed',
155
+ errorCode: 'UPGRADE_DOWNLOAD_FAILED',
156
+ phase: 'download',
157
+ targetVersion: '2.0.14',
158
+ });
159
+ });
160
+ });
@@ -0,0 +1,15 @@
1
+ // V1 plugin API cannot run on opencode2. Settings must mark this explicitly
2
+ // instead of letting load fail silently.
3
+ export const PLUGIN_COMPATIBILITY_V1_INCOMPATIBLE = 'v1-incompatible';
4
+
5
+ export function pluginCompatibilityForV2() {
6
+ return {
7
+ compatibility: PLUGIN_COMPATIBILITY_V1_INCOMPATIBLE,
8
+ compatible: false,
9
+ };
10
+ }
11
+
12
+ export function withV1PluginIncompatibility(record) {
13
+ if (!record || typeof record !== 'object') return record;
14
+ return { ...record, ...pluginCompatibilityForV2() };
15
+ }
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ PLUGIN_COMPATIBILITY_V1_INCOMPATIBLE,
4
+ pluginCompatibilityForV2,
5
+ withV1PluginIncompatibility,
6
+ } from './plugin-compatibility.js';
7
+
8
+ describe('V1 plugin compatibility (ticket 12)', () => {
9
+ it('marks V1 plugins incompatible instead of a silent load failure', () => {
10
+ expect(pluginCompatibilityForV2()).toEqual({
11
+ compatibility: PLUGIN_COMPATIBILITY_V1_INCOMPATIBLE,
12
+ compatible: false,
13
+ });
14
+ expect(withV1PluginIncompatibility({ id: 'config:user:oh-my-opencode', spec: 'oh-my-opencode' })).toEqual({
15
+ id: 'config:user:oh-my-opencode',
16
+ spec: 'oh-my-opencode',
17
+ compatibility: 'v1-incompatible',
18
+ compatible: false,
19
+ });
20
+ });
21
+ });
@@ -12,8 +12,6 @@ const BAD_REQUEST_CODES = new Set(['INVALID_FILENAME', 'INVALID_SCOPE', 'INVALID
12
12
  export const registerPluginRoutes = (app, dependencies) => {
13
13
  const {
14
14
  resolveOptionalProjectDirectory,
15
- refreshOpenCodeAfterConfigChange,
16
- clientReloadDelayMs,
17
15
  listPluginEntries,
18
16
  getPluginEntry,
19
17
  createPluginEntry,
@@ -45,9 +43,9 @@ export const registerPluginRoutes = (app, dependencies) => {
45
43
 
46
44
  const successPayload = (message) => ({
47
45
  success: true,
48
- requiresReload: true,
46
+ requiresReload: false,
47
+ application: 'watch',
49
48
  message,
50
- reloadDelayMs: clientReloadDelayMs,
51
49
  reloadFailed: false,
52
50
  warning: undefined,
53
51
  });
@@ -57,20 +55,7 @@ export const registerPluginRoutes = (app, dependencies) => {
57
55
 
58
56
  const pastTense = operation.replace(/ion$/, 'ed').replace(/update$/, 'updated');
59
57
 
60
- try {
61
- await refreshOpenCodeAfterConfigChange(`plugin ${operation}`);
62
- return res.json(successPayload(`Plugin ${pastTense}. Reloading interface…`));
63
- } catch (error) {
64
- console.error(`[API:plugin ${operation}] Reload failed after config write:`, error);
65
- return res.json({
66
- success: true,
67
- requiresReload: false,
68
- message: `Plugin ${pastTense}, but OpenCode reload failed.`,
69
- reloadDelayMs: clientReloadDelayMs,
70
- reloadFailed: true,
71
- warning: error.message || 'OpenCode reload failed after plugin config changed',
72
- });
73
- }
58
+ return res.json(successPayload(`Plugin ${pastTense}.`));
74
59
  };
75
60
 
76
61
  const validateEntryId = (id) => {