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

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 (311) 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-B_JQoreg.js} +1 -1
  7. package/dist/assets/AssistantView-ClQAfGOT.js +3 -0
  8. package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-BNnda8qO.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-VYQTSAkr.js} +1 -1
  10. package/dist/assets/{Icon-BgdfPhNs.js → Icon-BiEQEcih.js} +1 -1
  11. package/dist/assets/MarkdownRenderer-CJHSsWin.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-DwrHFGUp.js} +3 -3
  13. package/dist/assets/{MarkstreamRendererImpl-D2dsW5Lg.js → MarkstreamRendererImpl-CSnbQMu3.js} +1 -1
  14. package/dist/assets/MobileDetailNavigation-BXr6AGLT.js +1 -0
  15. package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-D7OE9OP_.js} +2 -2
  16. package/dist/assets/MobileSurface-D2jUkX9l.js +2 -0
  17. package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-DF3iIFCd.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-BjHmlwlw.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-DLtWO_Gp.js +2 -0
  20. package/dist/assets/SettingsView-BRxELjow.js +25 -0
  21. package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-BI5swspb.js} +1 -1
  22. package/dist/assets/{TerminalView-CDdzF4yi.js → TerminalView-BU3x8-fr.js} +1 -1
  23. package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-BEWhBcRd.js} +2 -2
  24. package/dist/assets/appThemeRegistry-4MR0F7Jz.js +1 -0
  25. package/dist/assets/{button-CI7Q-Zw_.js → button-DKuz3L8l.js} +1 -1
  26. package/dist/assets/collapsible-DXbDynN4.js +3 -0
  27. package/dist/assets/context-menu-CqolsLCm.js +1 -0
  28. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-CoDkOnyS.js} +1 -1
  29. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DDlVKyOE.js} +1 -1
  30. package/dist/assets/{diagram-DdLO8HZ9.js → diagram-D7T8HQgj.js} +1 -1
  31. package/dist/assets/{dialog-B6pnIYmp.js → dialog-DQYl3W_o.js} +1 -1
  32. package/dist/assets/es-CN3E_zXj.js +15 -0
  33. package/dist/assets/{fileMentionTouchSelection-DNeiqns-.js → fileMentionTouchSelection-BZ6ovBYH.js} +1 -1
  34. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-JrpcsOCs.js} +1 -1
  35. package/dist/assets/fr-9zZvQZD2.js +13 -0
  36. package/dist/assets/gitApi-CM42qUed.js +10 -0
  37. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-CIl0FL10.js} +1 -1
  38. package/dist/assets/{input-D-__fwRi.js → input-CzQy8UhV.js} +1 -1
  39. package/dist/assets/insertionBoundaries-CtkZ9720.js +1 -0
  40. package/dist/assets/ja-Dnjk_82N.js +15 -0
  41. package/dist/assets/ko-BxCEE99C.js +15 -0
  42. package/dist/assets/main-BILgBI3B.js +169 -0
  43. package/dist/assets/main-BeveiW6F.js +2 -0
  44. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  45. package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-Cj1I5NDw.js} +1 -1
  46. package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
  47. package/dist/assets/mcpQueries-D5mQymgM.js +1 -0
  48. package/dist/assets/miniChat-BgrzM3u7.js +2 -0
  49. package/dist/assets/mobile-DtYoQE8C.js +2 -0
  50. package/dist/assets/multirun-jy8dEI96.js +4 -0
  51. package/dist/assets/{persistence-Ce3AnpEr.js → persistence-C9LQpiOS.js} +2 -2
  52. package/dist/assets/pl-CNNsWnPM.js +15 -0
  53. package/dist/assets/platform-BQghHRfp.js +6 -0
  54. package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
  55. package/dist/assets/renderElectronMiniChatApp-BGrrmeME.js +2 -0
  56. package/dist/assets/renderMobileApp-u3c4GtFC.js +28 -0
  57. package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-BIGBRGQI.js} +1 -1
  58. package/dist/assets/{runtimeEndpointReset-ChOxt-J7.js → runtimeEndpointReset-D2savkuw.js} +12 -12
  59. package/dist/assets/{sessionChangeSummary-NnxcqCTI.js → sessionChangeSummary-eIbpSfrO.js} +1 -1
  60. package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-myHe5ltu.js} +1 -1
  61. package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-BQtmHx9r.js} +1 -1
  62. package/dist/assets/src-CLN7Z-zx.css +2 -0
  63. package/dist/assets/store-DLCiZaA6.js +15 -0
  64. package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-BxcdiduX.js} +1 -1
  65. package/dist/assets/{textarea-bHYxZtfH.js → textarea-CakKvqFw.js} +1 -1
  66. package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-Cx5gTbdY.js} +1 -1
  67. package/dist/assets/uk-rwQrxeYM.js +15 -0
  68. package/dist/assets/{url-_z7evzdK.js → url-CDHnjlsm.js} +1 -1
  69. package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-CNeo0aZs.js} +59 -45
  70. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
  71. package/dist/assets/useDesktopWindowControlsLayout-DBJ56qIo.js +1 -0
  72. package/dist/assets/useEffectiveDirectory-B4LvR8Gt.js +1 -0
  73. package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-CUnQ4TVm.js} +1 -1
  74. package/dist/assets/useMobileNavigationStore-ZHJPxlHt.js +414 -0
  75. package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
  76. package/dist/assets/useSessionAutoCleanup-BIVmrnYU.js +2 -0
  77. package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
  78. package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
  79. package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-CJZbJDW3.js} +2 -2
  80. package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
  81. package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
  82. package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
  83. package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
  84. package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
  85. package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
  86. package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
  87. package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
  88. package/dist/assets/vendor-opencode-client-BOe2qpQb.js +12 -0
  89. package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
  90. package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
  91. package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
  92. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
  93. package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
  94. package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
  95. package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
  96. package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
  97. package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
  98. package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
  99. package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
  100. package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
  101. package/dist/index.html +13 -13
  102. package/dist/mini-chat.html +13 -13
  103. package/dist/mobile.html +13 -13
  104. package/package.json +2 -2
  105. package/server/index.js +392 -20
  106. package/server/lib/assistants/DOCUMENTATION.md +26 -21
  107. package/server/lib/assistants/assign.js +2 -1
  108. package/server/lib/assistants/contact-store.js +37 -19
  109. package/server/lib/assistants/contracts.js +11 -1
  110. package/server/lib/assistants/projection-history.test.js +454 -0
  111. package/server/lib/assistants/routes.js +11 -5
  112. package/server/lib/assistants/service.js +850 -294
  113. package/server/lib/assistants/service.test.js +588 -274
  114. package/server/lib/conversations/DOCUMENTATION.md +5 -5
  115. package/server/lib/conversations/service.js +87 -57
  116. package/server/lib/conversations/service.test.js +132 -100
  117. package/server/lib/conversations/validation.js +11 -1
  118. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  119. package/server/lib/event-stream/global-hub.js +51 -5
  120. package/server/lib/event-stream/runtime.js +57 -20
  121. package/server/lib/event-stream/runtime.test.js +112 -0
  122. package/server/lib/llm/DOCUMENTATION.md +95 -84
  123. package/server/lib/llm/catalog.js +111 -45
  124. package/server/lib/llm/catalog.test.js +121 -59
  125. package/server/lib/llm/completions.js +41 -12
  126. package/server/lib/llm/completions.test.js +45 -4
  127. package/server/lib/llm/generate.js +516 -453
  128. package/server/lib/llm/generate.test.js +705 -484
  129. package/server/lib/llm/routes.js +8 -4
  130. package/server/lib/message-queue/DOCUMENTATION.md +5 -5
  131. package/server/lib/message-queue/opencode-adapter.js +191 -104
  132. package/server/lib/message-queue/opencode-adapter.test.js +328 -58
  133. package/server/lib/message-queue/runtime.js +15 -2
  134. package/server/lib/message-queue/runtime.test.js +6 -6
  135. package/server/lib/message-queue/worker.js +7 -1
  136. package/server/lib/notifications/runtime.js +5 -0
  137. package/server/lib/opencode/DOCUMENTATION.md +103 -16
  138. package/server/lib/opencode/agent-document.d.ts +28 -0
  139. package/server/lib/opencode/agent-document.js +280 -0
  140. package/server/lib/opencode/agent-document.test.js +137 -0
  141. package/server/lib/opencode/agents.js +96 -0
  142. package/server/lib/opencode/auth-state-runtime.js +5 -0
  143. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  144. package/server/lib/opencode/config-entity-routes.js +111 -102
  145. package/server/lib/opencode/config-entity-routes.test.js +123 -10
  146. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  147. package/server/lib/opencode/ensure-cli.js +238 -0
  148. package/server/lib/opencode/ensure-cli.test.js +119 -0
  149. package/server/lib/opencode/env-runtime.js +208 -111
  150. package/server/lib/opencode/env-runtime.test.js +211 -14
  151. package/server/lib/opencode/feature-routes-runtime.js +80 -5
  152. package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
  153. package/server/lib/opencode/http-server-connections.js +98 -0
  154. package/server/lib/opencode/http-server-connections.test.js +321 -0
  155. package/server/lib/opencode/lifecycle.js +539 -95
  156. package/server/lib/opencode/lifecycle.test.js +698 -6
  157. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  158. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  159. package/server/lib/opencode/network-runtime.js +28 -16
  160. package/server/lib/opencode/network-runtime.test.js +68 -4
  161. package/server/lib/opencode/openchamber-routes.js +7 -1
  162. package/server/lib/opencode/opencode2-pin.js +139 -0
  163. package/server/lib/opencode/opencode2-pin.test.js +88 -0
  164. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
  165. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  166. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  167. package/server/lib/opencode/plugin-compatibility.js +15 -0
  168. package/server/lib/opencode/plugin-compatibility.test.js +21 -0
  169. package/server/lib/opencode/plugin-routes.js +3 -18
  170. package/server/lib/opencode/plugin-routes.test.js +9 -9
  171. package/server/lib/opencode/plugins.js +4 -3
  172. package/server/lib/opencode/plugins.test.js +12 -0
  173. package/server/lib/opencode/provider-catalog.js +58 -12
  174. package/server/lib/opencode/provider-catalog.test.js +48 -0
  175. package/server/lib/opencode/proxy.js +368 -28
  176. package/server/lib/opencode/proxy.test.js +338 -0
  177. package/server/lib/opencode/routes.js +303 -76
  178. package/server/lib/opencode/routes.test.js +315 -50
  179. package/server/lib/opencode/runtime-contract.js +401 -0
  180. package/server/lib/opencode/runtime-contract.test.js +214 -0
  181. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  182. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  183. package/server/lib/opencode/server-utils-runtime.js +35 -0
  184. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  185. package/server/lib/opencode/shared-service.js +147 -0
  186. package/server/lib/opencode/shared-service.test.js +93 -0
  187. package/server/lib/opencode/shared.js +3 -3
  188. package/server/lib/opencode/shutdown-runtime.js +15 -6
  189. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  190. package/server/lib/opencode/skill-routes.js +26 -40
  191. package/server/lib/opencode/skill-routes.test.js +71 -7
  192. package/server/lib/opencode/skills.test.js +15 -0
  193. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  194. package/server/lib/opencode/static-routes-runtime.js +3 -1
  195. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  196. package/server/lib/opencode/v1-migration-gate.js +157 -0
  197. package/server/lib/opencode/v1-migration-gate.test.js +111 -0
  198. package/server/lib/opencode/v2-client.js +60 -0
  199. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  200. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  201. package/server/lib/opencode/watcher.js +2 -1
  202. package/server/lib/opencode/watcher.test.js +1 -1
  203. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  204. package/server/lib/permission-auto-accept/runtime.js +14 -7
  205. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  206. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  207. package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
  208. package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
  209. package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
  210. package/server/lib/scheduled-tasks/routes.js +27 -0
  211. package/server/lib/scheduled-tasks/routes.test.js +94 -0
  212. package/server/lib/scheduled-tasks/runtime.js +381 -310
  213. package/server/lib/scheduled-tasks/runtime.test.js +600 -309
  214. package/server/lib/session-goal/DOCUMENTATION.md +173 -22
  215. package/server/lib/session-goal/capability.js +57 -0
  216. package/server/lib/session-goal/capability.test.js +33 -0
  217. package/server/lib/session-goal/routes.js +240 -2
  218. package/server/lib/session-goal/routes.test.js +211 -0
  219. package/server/lib/session-goal/runtime.js +1617 -113
  220. package/server/lib/session-goal/runtime.test.js +1500 -70
  221. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  222. package/server/lib/session-index/event-ingest.js +89 -16
  223. package/server/lib/session-index/event-ingest.test.js +73 -0
  224. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  225. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  226. package/server/lib/session-index/routes.js +63 -10
  227. package/server/lib/session-index/routes.test.js +8 -8
  228. package/server/lib/session-index/service.js +7 -1
  229. package/server/lib/session-index/sync-runtime.js +171 -41
  230. package/server/lib/session-index/sync-runtime.test.js +164 -9
  231. package/server/lib/session-metadata/DOCUMENTATION.md +86 -0
  232. package/server/lib/session-metadata/routes.js +193 -0
  233. package/server/lib/session-metadata/routes.test.js +156 -0
  234. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  235. package/server/lib/session-metadata/session-archive.js +654 -0
  236. package/server/lib/session-metadata/session-archive.test.js +359 -0
  237. package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
  238. package/server/lib/session-metadata/session-metadata-store.js +375 -0
  239. package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
  240. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  241. package/server/lib/session-metadata/session-projection.js +255 -0
  242. package/server/lib/session-metadata/session-projection.test.js +64 -0
  243. package/server/lib/session-metadata/system-session.js +55 -0
  244. package/server/lib/session-metadata/system-session.test.js +55 -0
  245. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  246. package/server/lib/session-title/native-runtime.test.js +167 -0
  247. package/server/lib/session-title/runtime.js +67 -42
  248. package/server/lib/session-title/session-access.js +46 -0
  249. package/server/lib/session-title/session-access.test.js +91 -0
  250. package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
  251. package/server/lib/session-turn-pages/reconcile.service.js +1 -1
  252. package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
  253. package/server/lib/session-turn-pages/routes.js +123 -75
  254. package/server/lib/session-turn-pages/routes.test.js +566 -0
  255. package/server/lib/session-turn-pages/service.js +6 -2
  256. package/server/lib/session-turn-pages/service.test.js +34 -0
  257. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  258. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  259. package/server/lib/small-model/DOCUMENTATION.md +101 -146
  260. package/server/lib/small-model/catalog.js +84 -54
  261. package/server/lib/small-model/catalog.test.js +111 -9
  262. package/server/lib/small-model/custom-api.js +68 -0
  263. package/server/lib/small-model/custom-api.test.js +64 -0
  264. package/server/lib/small-model/index.js +125 -241
  265. package/server/lib/small-model/resolve.js +35 -120
  266. package/server/lib/small-model/resolve.test.js +20 -110
  267. package/server/lib/small-model/routes.js +1 -3
  268. package/server/lib/small-model/summary-settings.test.js +157 -253
  269. package/server/opencode-proxy.test.js +63 -12
  270. package/dist/assets/AssistantView-BUveAgc4.js +0 -3
  271. package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
  272. package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
  273. package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
  274. package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
  275. package/dist/assets/SettingsView-PkXIavr7.js +0 -25
  276. package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
  277. package/dist/assets/collapsible-D5Phaykh.js +0 -1
  278. package/dist/assets/context-menu-CubSDW2g.js +0 -1
  279. package/dist/assets/es-DA3MqOo0.js +0 -15
  280. package/dist/assets/fr-Dbz0CdIA.js +0 -13
  281. package/dist/assets/gitApi-DXyC4MiV.js +0 -7
  282. package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
  283. package/dist/assets/ja-DumanVQA.js +0 -15
  284. package/dist/assets/ko-C0IkiUjQ.js +0 -15
  285. package/dist/assets/main-BPS5ylib.js +0 -2
  286. package/dist/assets/main-Cpd9VWYs.js +0 -169
  287. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  288. package/dist/assets/miniChat-B8_KLWiY.js +0 -2
  289. package/dist/assets/mobile-CtktKrXn.js +0 -2
  290. package/dist/assets/multirun-CYfP9Cfm.js +0 -4
  291. package/dist/assets/pl-B9No6MQp.js +0 -15
  292. package/dist/assets/platform-0w_Q9_ji.js +0 -2
  293. package/dist/assets/projectMeta-S1hJ7oBp.js +0 -3
  294. package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
  295. package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
  296. package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
  297. package/dist/assets/src-rS3zCcsC.css +0 -2
  298. package/dist/assets/store-5B_LDnLM.js +0 -15
  299. package/dist/assets/uk-C_1KkqyP.js +0 -15
  300. package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
  301. package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
  302. package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
  303. package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
  304. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  305. package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
  306. package/dist/assets/zh-CN-uWILgBdt.js +0 -15
  307. package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
  308. package/server/lib/small-model/call.js +0 -427
  309. package/server/lib/small-model/call.test.js +0 -382
  310. package/server/lib/small-model/opencode-session.js +0 -339
  311. package/server/lib/small-model/opencode-session.test.js +0 -177
@@ -11,8 +11,10 @@ This module provides OpenCode server integration utilities for the web server ru
11
11
  - `packages/web/server/lib/opencode/cli-entry-runtime.js`: CLI entrypoint runtime that detects direct execution, forces `OPENCHAMBER_RUNTIME=web` unless already `ssh-remote` (so `dev:server` cannot inherit a leftover desktop env and host a relay, while SSH-managed remotes may), parses CLI options, and starts server bootstrap.
12
12
  - `packages/web/server/lib/opencode/routes.js`: OpenCode/provider settings and auth-related route registration.
13
13
  - `packages/web/server/lib/opencode/lifecycle.js`: OpenCode process lifecycle runtime (startup, restart, readiness, health monitoring). Managed child env inherits the user shell environment unchanged; experimental flags such as `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` follow the user's own environment and are never injected by OpenChamber.
14
+ - `packages/web/server/lib/opencode/v1-migration-gate.js`: pure V1→v2 migration admission gate (`GET /api/experimental/migration/v1` → whether transcript may be fetched).
14
15
  - `packages/web/server/lib/opencode/managed-capabilities-runtime.js`: managed-child scheduled-task resources, config injection, rotating bridge identity, and bridge authorization.
15
16
  - `packages/web/server/lib/opencode/env-runtime.js`: OpenCode CLI/binary resolution and shell environment runtime.
17
+ - `packages/web/server/lib/opencode/ensure-cli.js`: detect local `opencode` version and install the pinned official V2 into the OpenChamber data dir when missing or too old. Never uses a packaged/bundled binary.
16
18
  - `packages/web/server/lib/opencode/env-config.js`: OpenCode-related environment variable parsing and validation (host/port/hostname).
17
19
  - `packages/web/server/lib/opencode/hmr-state-runtime.js`: HMR-persistent runtime state initialization, auth-state bootstrap, and HMR sync helpers.
18
20
  - `packages/web/server/lib/opencode/bootstrap-runtime.js`: base app bootstrap runtime for status/auth/tts/notification/OpenChamber route wiring.
@@ -25,7 +27,7 @@ This module provides OpenCode server integration utilities for the web server ru
25
27
  - `packages/web/server/lib/opencode/core-routes.js`: server status/system routes, auth/access guard routes, and settings utility route registration.
26
28
  - `packages/web/server/lib/opencode/shutdown-runtime.js`: graceful shutdown orchestration runtime for watcher/session/terminal/process/server teardown.
27
29
  - `packages/web/server/lib/opencode/server-startup-runtime.js`: server listen/startup flow and process/signal handler orchestration runtime.
28
- - `packages/web/server/lib/opencode/static-routes-runtime.js`: static asset/SPA fallback route registration and manifest route wiring.
30
+ - `packages/web/server/lib/opencode/static-routes-runtime.js`: static asset/SPA fallback route registration and manifest route wiring. The SPA fallback sends `index.html` relative to `root`, so deep links keep working when the install lives under a dot directory.
29
31
  - `packages/web/server/lib/opencode/feature-routes-runtime.js`: feature route composition runtime for dynamic import-backed config/skill/provider route registration.
30
32
  - `packages/web/server/lib/conversations/`: combined create-session-and-prompt orchestration (OpenChamber-owned, registered before generic proxy).
31
33
  - `packages/web/server/lib/session-turn-pages/`: turn-window session message pagination (`GET /api/openchamber/sessions/:sessionID/messages`), anchor reconcile (`GET .../messages/reconcile`), Changes L2/L3 (`GET .../changes`), and exact message L1 projection (`GET /api/session/:sessionID/message/:messageID`); loops official OpenCode `session.messages` for turn pages and head→anchor gap recovery with continuation/budgets/`resetRequired`; all registered before generic proxy. L1 first-packet / SSE / exact-message paths keep `summary.diffs` as a slim `{ file, status?, additions, deletions }` list plus additive `diffCount`/`hasDiffs` (patch bodies stay on L3). See `session-turn-pages/DOCUMENTATION.md`.
@@ -83,8 +85,11 @@ This module provides OpenCode server integration utilities for the web server ru
83
85
  - `GET /api/config/opencode-resolution`
84
86
  - `GET /api/behavior/agents-md`: returns an authoritative empty document only when the file is absent (`ENOENT`); permission and I/O failures return HTTP 500.
85
87
  - `PUT /api/behavior/agents-md`
86
- - `POST /api/opencode/upgrade` (proxies OpenCode `POST /global/upgrade` with a required semantic version `target`, then restarts managed OpenCode so the new binary is active)
87
- - `GET /api/opencode/upgrade-status`
88
+ - `POST /api/opencode/upgrade` (ticket 12): installs the pinned/target `@opencode/cli` into the **owned cache** at `~/.config/openchamber/opencode-cli/<version>/`. Managed owned-cache processes restart on that binary; official shared services use that exact binary's `service restart`, then adopt its registration again. Success requires a fresh running-serve version probe and runtime contract verification. Arbitrary external servers, private processes using global CLI, and private bundled binaries return HTTP 409; user installs are never overwritten. Concurrent upgrades are single-flight (`operation` state). Active sessions require `confirmActiveTasks`/`force`. Failure preserves cached binaries, restores the previous binary selection, and reports the failed phase; it does not claim the old process was restored. Shared-service recovery can subsequently re-ensure the registered service.
89
+ - `GET /api/opencode/upgrade-status`: returns `currentVersion`/`serveVersion`/`cliVersion`, `targetVersion`/`latestVersion` (pin), `supplySource`, `ownership`, `canManage`, `management`, `guidance`, `contract`, and `operation`. Official shared services report `ownership: 'shared-service'`, `processOwnership: 'shared'`, and `canManage: true`. `available` is true only when `canManage` and target > current; unmanaged global/external runtimes report `available: null` plus manual guidance.
90
+ - `GET /api/opencode/contract` (ticket 11): runtime contract admission for the **running serve** (not health alone). Dimensions: reachable / authenticated / protocolCompatible / executionAllowed / capabilities / migration / CLI↔serve mismatch. Floor `2.0.12` (acceptable OpenCode 2.x at or above); below-min 2.x and 1.x block execution with upgrade guidance; no upper version cap.
91
+ - `GET /api/opencode/health`: probes managed OpenCode `GET /api/info` (Basic auth, 4s timeout). `healthy` is reachability/version shape via `evaluateOpenCodeHealthBody` (admits official 2.x `ServerInfo` and classic `{ healthy: true, version }`; rejects 1.x / missing / `healthy: false`). Response also includes `executionAllowed`, `protocolCompatible`, and `contract` so clients do not treat health success as full execution semantics. Upstream non-OK responses pass through status with `{ healthy: false, error, contract }`; transport failures return HTTP 503.
92
+ - `GET /api/opencode/version`: serve + CLI versions, mismatch flag, and contract snapshot
88
93
  - `POST /api/opencode/directory`
89
94
  - `GET /api/provider/:providerId/source`
90
95
  - `DELETE /api/provider/:providerId/auth`
@@ -112,26 +117,74 @@ This module provides OpenCode server integration utilities for the web server ru
112
117
  membership — that remains a UI-side authoritative pull fused with directory
113
118
  `/session/status`.
114
119
 
120
+ ## Public exports (opencode2-pin.js)
121
+ - `PINNED_OPENCODE2_VERSION`: exact desktop/runtime pin (never 1.x).
122
+ - `OPENCODE2_NPM_PACKAGE`: `@opencode/cli` (global install that ships the official `opencode` v2 binary; not 1.x `opencode-ai`).
123
+ - `parseOpenCode2VersionOutput(stdout)` / `compareOpenCode2Versions(left, right)` / `isOpenCode2VersionAtLeast(version, minimum)`: CLI `--version` parse and pin comparison.
124
+ - `openCode2BinaryName(platform)` / `npmPackageForOpenCode2(platform, arch)`: staged binary name `opencode` (`opencode.exe` on Windows) and official platform package `@opencode/cli-<os>-<arch>[-baseline]`.
125
+ - `isOpenCode1xVersion(value)` / `isAcceptableOpenCode2HealthVersion(value)` / `evaluateOpenCodeHealthBody(body)`: health admission accepts only 2.x. 1.x, `0.x` pre-2 binaries, and missing/unknown versions are rejected even when `healthy: true`.
126
+ - `resolveOpenCode2UpgradeTarget(target)` / `rejectOpenCode1xUpgradeTarget(target)`: upgrade targets default to the pin and refuse 1.x.
127
+
128
+ ## Public exports (runtime-contract.js)
129
+ - Ticket 11 pure admission matrix. `RUNTIME_CONTRACT_MIN_VERIFIED` (`2.0.12`) is the documented 2.x floor; capabilities include `core.protocol`, `session.prompt`, `session.queuedInput`, `session.background`, `session.generationFallback`, `migration.v1Read`.
130
+ - `evaluateRuntimeContract(input)`: separate reachable / auth / protocol / execution / migration / CLI↔serve mismatch reasons for UI and diagnostics. **`executionAllowed` requires acceptable OpenCode 2.x at or above the floor** (`versionBand === 'verified'`).
131
+ - `formatRuntimeContractBlockedError(contract)` / `runtimeContractExecutionBlockedBody(contract)`: user-facing upgrade guidance on HTTP 409 execution blocks (below-min / 1.x), not raw JSON dumps.
132
+ - `isRuntimeContractExecutionPath` / `isRuntimeContractDiagnosticPath` / `isRuntimeContractStopPath` / `shouldBlockRuntimeContractExecution` / `createRevokedRuntimeContract`: proxy + Host transport gate helpers. **Execution paths require `executionAllowed === true` on the current contract**; null/pending/false all block (no silent bypass). Unit/DI factories may pass `{ allowMissingContract: true }` only when no lifecycle is wired. Interrupt/abort and reads stay available when execution is limited. Health success is not full execution semantics.
133
+
134
+ ## Public exports (server-opencode-fetch.js)
135
+ - Host transport boundary so queue / goal / scheduled / assistants / session-title cannot bypass the proxy write gate.
136
+ - `configureServerOpenCodeFetchGate(getter, options?)` / `createServerOpenCodeFetch` / `wrapFetchWithRuntimeContractGate`: shared admission; `makeOpenCodeV2Client` always wraps fetch with the gate. Production must not set `allowMissingContract`.
137
+
138
+ ## Public exports (owned-runtime-upgrade.js)
139
+ - Ticket 12 ownership model: `classifyRuntimeOwnership` separates process ownership (external vs managed) from binary ownership (owned-cache / global-cli / bundled / env / settings). External serve using an owned file stays external; managed process using a global file stays global-cli.
140
+ - `buildUpgradeStatusSnapshot` / `evaluateOwnedUpgradeResult` / `createUpgradeOperationState` / `resolveOwnedCacheBinaryPath`.
141
+
142
+ ## Public exports (ensure-cli.js)
143
+ - `ensurePinnedOpenCode2Cli({ discoveredPath, pin, autoInstall, install, readVersion })`: reuse any discovered acceptable 2.x CLI (official name `opencode`). Install the pin into `~/.config/openchamber/opencode-cli/<pin>/opencode` only when nothing usable is installed. `OPENCHAMBER_OPENCODE2_AUTO_INSTALL=0` disables download (`OPENCODE_CLI_MISSING`).
144
+ - `installPinnedOpenCode2Cli(options)`: download `@opencode/cli-<os>-<arch>@pin`, extract, copy as `opencode`, chmod, verify `--version`.
145
+ - `installedOpenCode2BinaryPath(version)` / `readOpenCode2BinaryVersion(path)` / `isOpenCode2AutoInstallEnabled()`.
146
+
147
+ ## Public exports (v1-migration-gate.js)
148
+ - `OPENCODE_V1_MIGRATION_PATH`: `/api/experimental/migration/v1`.
149
+ - `V1_MIGRATION_USER_NOTICE`: user-visible backfill notes (reuse message ids; in-progress tools become interrupted; V1 subtasks omitted from v2).
150
+ - `evaluateV1MigrationGate(input)`: pure function. Accepts migration JSON (`{ status }`), an HTTP envelope (`{ httpStatus|status, body }`), or a transport failure (`{ error }`). Returns `{ admitTranscript, phase, progress?, error?, userNotice? }`. `required` / `running` / `error` / network failure → `admitTranscript: false`. `completed` or HTTP 404 (no V1 library) → `true`. Unknown or empty bodies fail closed and are never treated as an empty-list success.
151
+ - `fetchV1MigrationGate({ url, headers, signal, fetchImpl })`: GET-only status poll used by lifecycle.
152
+
115
153
  ## Public exports (lifecycle.js)
116
- - `createOpenCodeLifecycleRuntime(dependencies)`: creates lifecycle runtime for managed/external OpenCode process orchestration.
154
+ - `createOpenCodeLifecycleRuntime(dependencies)`: creates lifecycle runtime for managed/external OpenCode process orchestration. Bootstrap reuses a healthy v2 `opencode serve` when present; otherwise starts managed `opencode serve`. Before managed spawn, `ensurePinnedOpenCode2CliEnv()` detects the local CLI version and installs the pinned official V2 when missing or too old (skipped for external skip-start). Startup accepts both `server listening on http://127.0.0.1:PORT` and the legacy `opencode server listening on …` line. Readiness/health probes `GET /api/info` first, then `/global/health`, with Basic auth (username `opencode`). A body is admitted only when `evaluateOpenCodeHealthBody` accepts the version (rejects 1.x and missing/unknown). After health ok, `startOpenCode` / `waitForOpenCodeReady` poll `GET /api/experimental/migration/v1` (no POST; backfill is owned by opencode2). Each readiness attempt uses one abort timer that covers **both** health and migration so a hung migration cannot outrun the per-attempt budget. External instances are a read-only mount: restart/refresh only re-probe health + V1 migration (never kill, spawn, clear port, or flip `isExternalOpenCode`); health monitoring skips them; upgrade routes return 409. After readiness admits, lifecycle warms recent directories via optional `getWarmupDirectories` (sequential best-effort `GET /api/session?directory=&limit=1`). External skip-start / auto-detect attach leave `isOpenCodeReady` false until the gate admits. `isOpenCodeReady` means transcript may be fetched only when the gate admits (`completed`, or no V1 library such as HTTP 404). `required` / `running` / `error` keep the ready gate closed; `error` is retried. The last gate result is stored on `state.v1Migration` and published on the OpenChamber `/health` snapshot so UI can render `phase` and running `progress` (`label` / `numerator` / `denominator`) plus `userNotice` (reuse message ids; in-progress tools become interrupted; V1 subtasks do not appear in v2).
155
+ - Shared official service (default for Web and Electron hosts, including mobile clients connected to those hosts): when `sharedService` is injected and neither an OpenCode port nor host is configured, bootstrap runs `opencode service start` before HMR/private-process reuse or any port-4096 probe. It attaches to the registered stable-channel service, adopts its password/base URL/port, and marks it external for shutdown. Closing OpenChamber leaves that service running; no managed capabilities (scheduled-task plugin, bridge token, `v2-plugin-host-shim`) are injected. Attach failure clears URL/port/password, reports an unavailable integration and permits explicit retry, without silently spawning a private server. Explicit host/port and shared-service opt-out retain their existing behavior; VS Code retains its extension-owned sidecar lifecycle.
156
+ - While attached, ordinary `restartOpenCode()` re-runs `service start` and adopts the latest registration before re-probing; a failed ensure propagates instead of probing stale credentials as success. Upgrade-only `restartOpenCode({ binaryPath })` bypasses settings rediscovery and runs the verified target's `service restart`. Configuration refresh uses `POST /api/location/reload` instead of process replacement (returns `{ reloaded: true, external: true }`). Health monitoring uses registration-PID liveness. Normal attachment/recovery does not version-pin or downgrade a healthy registered service; explicit in-app upgrade is the version-replacement operation.
117
157
  - Returned API:
118
158
  - `startOpenCode()`
119
159
  - `restartOpenCode()`
160
+ - `isSharedOpenCodeService()`: true while attached to the shared official service.
120
161
  - `waitForOpenCodeReady(timeoutMs?, intervalMs?)`
121
162
  - `waitForAgentPresence(agentName, timeoutMs?, intervalMs?)`
122
163
  - `refreshOpenCodeAfterConfigChange(reason, options?)`
123
164
  - `bootstrapOpenCodeAtStartup()`
124
165
  - `retryOpenCodeStartup()`
125
166
  - `startHealthMonitoring(healthCheckIntervalMs)`
126
- - `waitForPortRelease(port, timeoutMs, hostname?)`
127
- - `killProcessOnPort(port)`
167
+ - `waitForPortRelease(port, timeoutMs, hostname?)`: probe-only; reports whether the listener freed without killing occupants.
168
+ - `killProcessOnPort(port, ownedPid?)`: legacy helper retained for tests/compat; force-signals only an explicit owned pid/process group when `ownedPid` is provided. Restart and graceful shutdown **do not** call it after `child.close()` (close owns escalation; a second kill of a captured pid risks recycling). Never mass-kills by port via `lsof`.
169
+
170
+ ## Public exports (shared-service.js)
171
+ - Discovery and start of the official `opencode serve --service` background service shared with official Desktop/TUI (one process, one database, so providers and plugins stay in sync across clients).
172
+ - `resolveSharedServiceRegistrationPath(env?, homeDir?)`: `${XDG_STATE_HOME || ~/.local/state}/opencode/service.json` (stable channels only).
173
+ - `isSharedServiceEnabled(env?)`: on by default; `OPENCHAMBER_OPENCODE_SHARED_SERVICE=0|false|off` opts out.
174
+ - `parseSharedServiceRegistration(text)` / `readSharedServiceRegistration({ file, fsLike })`: `{ origin, port, pid, password, version }` or `null`; missing/malformed files are absent, never a service.
175
+ - `buildSharedServiceStartEnv(env)`: strips `OPENCHAMBER_*`, `OPENCODE_SERVER_PASSWORD`, and `OPENCODE_PASSWORD` so the long-lived service never inherits OpenChamber credentials.
176
+ - `startSharedOpenCodeService({ binary, args, env, spawnImpl, timeoutMs, replace })`: runs `<binary> [...args] service start`, or `service restart` for explicit `replace: true`; rejects with the stderr tail on non-zero exit or timeout. The launcher has hidden Windows console and does not own the detached service lifetime.
177
+ - `isProcessAlive(pid)`, `createSharedOpenCodeService({ envLike, fsLike, spawnImpl })`: the lifecycle dependency (`start`, `readRegistration`, `buildStartEnv`).
178
+ - `shared-service.integration.test.js` is opt-in via `OPENCHAMBER_TEST_OPENCODE_BINARY`; it isolates HOME/XDG/config/database and verifies launcher exit survival, PID reuse, explicit replacement and authenticated health, then stops only its isolated service.
128
179
 
129
180
  ## Public exports (env-runtime.js)
130
- - `createOpenCodeEnvRuntime(dependencies)`: creates runtime that owns OpenCode CLI environment and binary discovery state.
181
+ - `createOpenCodeEnvRuntime(dependencies)`: creates runtime that owns OpenCode CLI environment and binary discovery state. Discovery order: settings/env, then PATH `opencode`, then known install locations, then a previously installed pin under the OpenChamber data dir. Automatic discovery and bundled resources use only `opencode` (`opencode.exe` on Windows); 1.x binaries are skipped by `--version`, not by basename. `resolveOpencodeCliPath()` never prefers a packaged/bundled Electron extraResource. Managed startup calls `ensurePinnedOpenCode2CliEnv()` and installs the pin only when no usable 2.x CLI exists; if pin install fails (or auto-install is disabled), it may last-resort to a 2.x binary under `OPENCHAMBER_BUNDLED_OPENCODE_CLI_DIR` or `process.resourcesPath/opencode-cli` (source `bundled`). Explicit `OPENCODE_BINARY` that reports 1.x fails closed with `OPENCODE_BINARY_INVALID`. Bootstrap reuses a healthy v2 `opencode serve` on port 4096 when present; otherwise it starts generic `opencode serve`.
182
+ - VS Code keeps a copied sidecar (`packages/vscode/src/opencode-sidecar.ts`) with the same invariants: discovery order may differ (no Electron bundled fallback), but reject list, listening lines, health path order (`/api/health` then `/global/health`), `{ healthy: true }`, and Basic username `opencode` must stay aligned. Changing one copy requires changing the other.
131
183
  - Returned API:
132
184
  - `applyLoginShellEnvSnapshot()`
133
185
  - `getLoginShellEnvSnapshot()`
134
186
  - `ensureOpencodeCliEnv()`
187
+ - `ensurePinnedOpenCode2CliEnv()`
135
188
  - `applyOpencodeBinaryFromSettings()`
136
189
  - `resolveOpencodeCliPath()`
137
190
  - `resolveManagedOpenCodeLaunchSpec(opencodePath)`: resolves the effective managed OpenCode launch target, unwrapping Windows package-manager shims to a direct native binary or explicit runtime+script when possible.
@@ -141,11 +194,12 @@ This module provides OpenCode server integration utilities for the web server ru
141
194
  - `isExecutable(filePath)`
142
195
  - `searchPathFor(binaryName)`
143
196
  - `clearResolvedOpenCodeBinary()`
197
+ - `forceResolvedOpenCodeBinary(path, source?)`: pin managed launch to an explicit binary (owned-cache upgrade) so restart does not rediscover a global CLI
144
198
 
145
199
  ## Public exports (managed-capabilities-runtime.js)
146
200
  - `createManagedCapabilitiesRuntime(dependencies?)`: returns resource publishing, bridge-origin, child-environment, identity, and bridge-authorization APIs for managed OpenCode only.
147
201
  - `mergeManagedOpenCodeConfig({ configContent, pluginUrl, instructionsUrl })`: merges injected plugin/instructions URLs with stable deduplication.
148
- - Managed child config injects the plugin as a file URL and instructions as an absolute filesystem path. Capability identity validates the current managed PID and its liveness before HMR reuse or bridge authorization.
202
+ - Managed child config injects the scheduled-task plugin as a file URL and instructions as an absolute filesystem path, then appends the `v2-plugin-host-shim` directory. OpenCode 2.0.12 has no `ctx.catalog`; that shim re-invokes catalog-based user plugins against `provider.transform`. Capability identity validates the current managed PID and its liveness before HMR reuse or bridge authorization.
149
203
  - `bootstrap-runtime.js` forwards managed bridge authorization to the API auth gate. The reserved bridge path accepts only the current managed-child capability and returns HTTP 403 before UI, tunnel, or client authentication for every other request. `feature-routes-runtime.js` registers the managed scheduled-task bridge before generic OpenCode proxy composition.
150
204
  - Capability resources use an application-version and source-content SHA-256 fingerprint directory. The bridge token is a managed OpenCode process capability: every plugin loaded into that process belongs to the trusted-code boundary. External OpenCode receives no managed capability injection or bridge authorization.
151
205
 
@@ -175,7 +229,7 @@ This module provides OpenCode server integration utilities for the web server ru
175
229
  ## Public exports (network-runtime.js)
176
230
  - `createOpenCodeNetworkRuntime(dependencies)`: creates runtime for OpenCode network and URL concerns.
177
231
  - Returned API:
178
- - `waitForReady(url, timeoutMs?)`
232
+ - `waitForReady(url, timeoutMs?)`: probes `GET /api/health` then `/global/health` with `getOpenCodeAuthHeaders()`; `{ healthy: true }` is ready.
179
233
  - `normalizeApiPrefix(prefix)`
180
234
  - `setDetectedOpenCodeApiPrefix()`
181
235
  - `buildOpenCodeUrl(path, prefixOverride?)`
@@ -233,11 +287,36 @@ This module provides OpenCode server integration utilities for the web server ru
233
287
  - `resolveProjectDirectory(req)`
234
288
  - `resolveOptionalProjectDirectory(req)`
235
289
 
290
+ ## V2 configuration persistence and activation
291
+
292
+ Agent, Command, MCP, Plugin and Skill mutation routes write their existing local
293
+ configuration paths and return `{ success: true, requiresReload: false,
294
+ application: 'watch' }` (skill install retains its `ok` result). They do not call
295
+ the process lifecycle or `location.reload`. V2 Config watchers and plugin/domain
296
+ reconciliation apply watched changes; frontend domain events refresh catalogs.
297
+ The receipt means **persisted**, not confirmed activation. A remote external
298
+ OpenCode with a different filesystem cannot see Host-local writes merely because
299
+ the write succeeded; restarting that server does not transfer those files either.
300
+
301
+ Raw global `opencode` config saves no longer request a restart. Opaque plugin-owned
302
+ JSON targets retain `requiresManualRestart: true` because OpenCode's config watcher
303
+ does not guarantee those plugins reload their private configuration. Explicit UI
304
+ configuration reload uses official SDK `location.reload`. The historical
305
+ `/api/config/reload` host/bridge endpoint remains reserved for existing process
306
+ startup/recovery/binary-change callers, not ordinary Settings mutations.
307
+
308
+ `config-hot-reload.integration.test.js` is opt-in via
309
+ `OPENCHAMBER_TEST_OPENCODE_BINARY=<absolute-v2-cli>`. It starts a separate loopback
310
+ server with isolated HOME/XDG/config/database, checks global command changes,
311
+ plugin disable/re-enable, credentials and MCP config changes without reload or
312
+ restart, then terminates only its own child and removes its temporary files.
313
+ It makes no model requests and never reads real user credentials.
314
+
236
315
  ## Public exports (config-entity-routes.js)
237
316
  - `registerConfigEntityRoutes(app, dependencies)`: registers configuration entity routes:
238
- - `GET /api/config/catalog/providers` resolves the request project directory, calls OpenCode's SDK `config.providers`, and returns schema version `1` with an allowlisted provider/model catalog. The route is available through the shared web host used by Web, Electron, hosted mobile, and Capacitor mobile, before generic OpenCode proxy handling.
239
- - Provider catalog responses include only provider `id`, `name`, and safe models. Safe models include `id`, `name`, fixed text/audio/image/video/pdf capability modalities, bounded cost/limit fields, `release_date`, and variant names with `{}` values. Identifiers are bounded to 512 characters, display names to 1024, release dates to 64, numeric values to absolute 1e9, and catalog collections to 200 providers, 500 models per provider, and 100 defaults or variants. Provider credentials/configuration and unallowlisted model fields stay server-side. Malformed catalog roots and SDK error envelopes return HTTP 502. `partial: true` is structural only: provider/model/default truncation, dropped providers/models/defaults, or truncated variant dictionaries. Soft allowlist stripping of optional metadata (unknown modalities, non-boolean capability flags, out-of-range cost/limit numbers, empty/null/invalid `release_date`) keeps valid models and must not set `partial: true`, or UI refresh retains a stale complete snapshot after OpenCode provider updates.
240
- - Agents: `/api/config/agents/:name` and `/api/config/agents/:name/config`
317
+ - `GET /api/config/catalog/providers` resolves the request project directory, composes official v2 `provider.list` + `model.list` (+ `model.default`), and returns schema version `1` with an allowlisted provider/model catalog. Catalog model ids and defaults use `ModelInfo.id` (the external generate/session id). `ModelInfo.modelID` is an internal field and is not the catalog key. The route is available through the shared web host used by Web, Electron, hosted mobile, and Capacitor mobile, before generic OpenCode proxy handling.
318
+ - Provider catalog responses include only provider `id`, `name`, and safe models. Safe models include `id`, `name`, fixed text/audio/image/video/pdf capability modalities, bounded cost/limit fields, `release_date`, and variant names with `{}` values. v2 `ModelInfo.variants` is an array of `{ id, settings?, headers?, body? }`; projection keeps ids in array order as empty-object keys and drops request payloads. v2 `capabilities.tools` maps to `toolcall`, and `input`/`output` modality name arrays map to boolean maps. v2 `cost` tier arrays keep the untiered base price. Identifiers are bounded to 512 characters, display names to 1024, release dates to 64, numeric values to absolute 1e9, and catalog collections to 200 providers, 500 models per provider, and 100 defaults or variants. Provider credentials/configuration and unallowlisted model fields stay server-side. Malformed catalog roots and SDK error envelopes return HTTP 502. `partial: true` is structural only: provider/model/default truncation, dropped providers/models/defaults, or truncated variant dictionaries. Soft allowlist stripping of optional metadata (unknown modalities, non-boolean capability flags, out-of-range cost/limit numbers, empty/null/invalid `release_date`, invalid variant ids) keeps valid models and must not set `partial: true`, or UI refresh retains a stale complete snapshot after OpenCode provider updates. The same input shapes are accepted by the VS Code projector and `packages/ui/src/lib/configCatalogParser.ts`.
319
+ - Agents: `/api/config/agents/:name` and `/api/config/agents/:name/config`. A `native` create or update writes only the fields in that object into the selected user or project markdown file. `scope` chooses the file. `confirmDrop` removes fields the current format cannot represent; every other field already in the file stays unchanged.
241
320
  - Commands: batched metadata via `POST /api/config/commands/metadata`; `{ catalog: true }` returns the compact autocomplete catalog without templates, plus CRUD at `/api/config/commands/:name`
242
321
  - Global raw configs: `GET /api/config/global` discovers existing config targets; `GET/PUT /api/config/global/:target` reads and writes `opencode`, `oh-my-opencode-slim`, and `oh-my-openagent` JSON or JSONC files
243
322
  - MCP servers: `/api/config/mcp` and `/api/config/mcp/:name`
@@ -259,6 +338,7 @@ When adding or changing Host HTTP APIs that mobile/desktop clients reach over Pr
259
338
  - `getOpenCodeAuthHeaders()`
260
339
  - `isOpenCodeConnectionSecure()`
261
340
  - `ensureLocalOpenCodeServerPassword(options?)`
341
+ - `adoptOpenCodeServerPassword(password, source)`: adopts a password owned by another process (the shared official service registration); `null` clears it on attach rollback.
262
342
 
263
343
  ## Public exports (core-routes.js)
264
344
  - `registerServerStatusRoutes(app, dependencies)`: registers status/system endpoints:
@@ -313,9 +393,13 @@ When adding or changing Host HTTP APIs that mobile/desktop clients reach over Pr
313
393
  - `setupProxy(app)`
314
394
 
315
395
  ## Public exports (shutdown-runtime.js)
316
- - `createGracefulShutdownRuntime(dependencies)`: creates graceful shutdown runtime for managed OpenCode and web server teardown sequencing.
396
+ - `createGracefulShutdownRuntime(dependencies)`: creates graceful shutdown runtime for managed OpenCode and web server teardown sequencing. Stops managed OpenCode only via `openCodeProcess.close()` (that handle owns SIGTERM→SIGKILL). After close, `waitForPortRelease(port, 5000)` probes whether the listener freed — never re-kills a pre-captured numeric pid (recycle hazard) and never mass-kills by port. When `forceCloseConnections: true`, after `server.close()` it runs `forceCloseHttpServerConnections` (`closeAllConnections` plus destroy of tracked upgraded sockets) so the HTTP close callback can fire without waiting `SHUTDOWN_TIMEOUT` on leftover WS/SSE.
317
397
  - Returned API:
318
- - `gracefulShutdown(options?)`: accepts `forceCloseConnections: true` for runtimes that close remaining HTTP connections after initiating server close.
398
+ - `gracefulShutdown(options?)`: accepts `forceCloseConnections: true` for runtimes that close remaining HTTP connections after initiating server close (including upgraded sockets tracked by `attachHttpServerConnectionTracker`).
399
+
400
+ ## Public exports (http-server-connections.js)
401
+ - `attachHttpServerConnectionTracker(server)`: tracks sockets from the server's `connection` event (covers later upgrades). Returns `{ getOpenSocketCount, destroyRemainingSockets, dispose }`.
402
+ - `forceCloseHttpServerConnections(server)`: `closeAllConnections` then destroys remaining tracked sockets; returns `{ destroyedTracked }`. Only the owning server's sockets — never an external server.
319
403
 
320
404
  ## Public exports (server-startup-runtime.js)
321
405
  - `createServerStartupRuntime(dependencies)`: creates runtime for server bind/listen and process handler wiring.
@@ -370,6 +454,8 @@ When adding or changing Host HTTP APIs that mobile/desktop clients reach over Pr
370
454
  - `POST /api/projects/:projectId/icon/discover`
371
455
 
372
456
  ## Public exports (skill-routes.js)
457
+
458
+ The V2 skill catalog uses `id` as the existing OpenChamber `name` invocation key and `path` as the file location. Frontmatter `name` is a display label, never an invocation key. Local fallback discovery uses the same path-derived ID, so live and local entries deduplicate correctly. Web, Electron and mobile share this route; VS Code mirrors the projection through `skill.list` in its extension host. Upstream failures remain errors rather than empty successful catalogs.
373
459
  - `registerSkillRoutes(app, dependencies)`: registers skills-related routes:
374
460
  - Skills config CRUD and metadata under `/api/config/skills*`; `summary=true` returns compact autocomplete fields without skill content or sources
375
461
  - Skills catalog listing/source pagination, scan, and install routes
@@ -382,9 +468,10 @@ When adding or changing Host HTTP APIs that mobile/desktop clients reach over Pr
382
468
  - SSE forwarders: `GET /api/global/event`, `GET /api/event` (optional `includeReasoning=false` parse-filters reasoning events on the Host send path; param never forwarded upstream; enabled path remains byte passthrough)
383
469
  - Official session.messages list: `GET /api/session/:sessionID/message` (optional `includeReasoning=false` strips reasoning parts; exact message GET is owned by session-turn-pages)
384
470
  - Session message forwarder: `POST /api/session/:sessionId/message`
385
- - Generic `/api/*` forwarding with hop-by-hop header filtering
471
+ - Generic `/api/*` forwarding with hop-by-hop header filtering. v2 upstream routes live under `/api` (`@opencode-ai/protocol`); the proxy restores that prefix after Express strips the `/api` mount. Forwarding `/agent` (v1 root) hits the v2 Web UI HTML fallback.
386
472
  - Windows `/session` merge fallback path behavior
387
- - OpenCode readiness gate for proxied `/api` requests
473
+ - OpenCode readiness gate for proxied `/api` requests: process warmup still uses `OPEN_CODE_READY_GRACE_MS`; `state.v1Migration.admitTranscript !== true` holds with no grace until the gate admits or the client cancels, so a `running` migration past 12s cannot `next()` an empty session list through as success
474
+ - Runtime contract execution gate (ticket 11): mutating session/turn paths require **current-instance** `executionAllowed === true` (null/pending/false → HTTP 409 `RUNTIME_CONTRACT_EXECUTION_BLOCKED`). The gate runs before readiness hold and **again after hold** so a stale permit cannot forward once the new instance is incompatible. Lifecycle **revokes** the permit (generation bump + pending contract) on start/restart; async health probes only publish when their generation still matches. Diagnostic GET health/info/migration paths, ordinary reads, and **interrupt/abort (stop-task)** stay available. Host background clients share the same gate via `server-opencode-fetch` / gated `makeOpenCodeV2Client`.
388
475
 
389
476
  ## Public exports (watcher.js)
390
477
  - `createOpenCodeWatcherRuntime(dependencies)`: creates global event watcher runtime backed by the shared upstream SSE reader.
@@ -0,0 +1,28 @@
1
+ export class DropConfirmationRequired extends Error {
2
+ code: 'drop-confirmation';
3
+ dropped: Array<{ key: string; reason: string }>;
4
+ constructor(dropped: Array<{ key: string; reason: string }>);
5
+ }
6
+
7
+ export function inspectAgentConfig(config: unknown): {
8
+ legacy: boolean;
9
+ dropped: Array<{ key: string; reason: string }>;
10
+ };
11
+
12
+ export function convertAgentConfig(config: unknown): {
13
+ frontmatter: Record<string, unknown>;
14
+ body: string;
15
+ legacy: boolean;
16
+ dropped: Array<{ key: string; reason: string }>;
17
+ };
18
+
19
+ export function applyNativePatch(
20
+ current: { frontmatter: Record<string, unknown>; body: string },
21
+ patch: Record<string, unknown>,
22
+ ): { frontmatter: Record<string, unknown>; body: string };
23
+
24
+ export function writeAgentDocument(
25
+ existing: { frontmatter?: Record<string, unknown>; body?: string },
26
+ patch: Record<string, unknown>,
27
+ confirmDrop: boolean,
28
+ ): { frontmatter: Record<string, unknown>; body: string };
@@ -0,0 +1,280 @@
1
+ /**
2
+ * OpenCode v2 agent markdown.
3
+ *
4
+ * A file is native only when every frontmatter key is a current agent field.
5
+ * Any older key makes the loader migrate the whole file, and native keys in
6
+ * that file are then treated as request body. Saving therefore rewrites the
7
+ * whole file as native markdown. Fields the runtime does not apply are reported
8
+ * and kept until the caller confirms they can be dropped.
9
+ */
10
+
11
+ const NATIVE_KEYS = new Set([
12
+ 'model',
13
+ 'variant',
14
+ 'request',
15
+ 'system',
16
+ 'description',
17
+ 'mode',
18
+ 'hidden',
19
+ 'color',
20
+ 'steps',
21
+ 'disabled',
22
+ 'permissions',
23
+ ]);
24
+
25
+ const ACTION_RENAMES = {
26
+ bash: 'shell',
27
+ task: 'subagent',
28
+ write: 'edit',
29
+ patch: 'edit',
30
+ };
31
+
32
+ const HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
33
+ const PROMPT_FILE = /^\{file:.+\}$/i;
34
+ const EFFECTS = new Set(['allow', 'ask', 'deny']);
35
+
36
+ export class DropConfirmationRequired extends Error {
37
+ /**
38
+ * @param {Array<{ key: string, reason: string }>} dropped
39
+ */
40
+ constructor(dropped) {
41
+ super('Drop confirmation required');
42
+ this.code = 'drop-confirmation';
43
+ this.dropped = dropped;
44
+ }
45
+ }
46
+
47
+ const isEffect = (value) => typeof value === 'string' && EFFECTS.has(value);
48
+
49
+ const renameAction = (action) => ACTION_RENAMES[action] || action;
50
+
51
+ /**
52
+ * @param {unknown} config
53
+ * @returns {{ legacy: boolean, dropped: Array<{ key: string, reason: string }> }}
54
+ */
55
+ export function inspectAgentConfig(config) {
56
+ const source = config && typeof config === 'object' && !Array.isArray(config) ? config : {};
57
+ /** @type {Array<{ key: string, reason: string }>} */
58
+ const dropped = [];
59
+ const keys = Object.keys(source);
60
+
61
+ for (const key of keys) {
62
+ if (key === 'temperature' || key === 'top_p') {
63
+ dropped.push({ key, reason: 'generation' });
64
+ continue;
65
+ }
66
+ if (key === 'options' && source.options && typeof source.options === 'object' && Object.keys(source.options).length > 0) {
67
+ dropped.push({ key, reason: 'unknown' });
68
+ continue;
69
+ }
70
+ if (key === 'prompt' && typeof source.prompt === 'string' && PROMPT_FILE.test(source.prompt.trim())) {
71
+ dropped.push({ key, reason: 'prompt-file' });
72
+ continue;
73
+ }
74
+ if (key === 'color' && source.color != null && source.color !== '' && !HEX_COLOR.test(String(source.color))) {
75
+ dropped.push({ key, reason: 'color' });
76
+ continue;
77
+ }
78
+ if (key === 'variant' && (typeof source.model !== 'string' || source.model.length === 0)) {
79
+ dropped.push({ key, reason: 'variant' });
80
+ continue;
81
+ }
82
+ if (!NATIVE_KEYS.has(key) && !isConvertibleKey(key)) {
83
+ dropped.push({ key, reason: 'unknown' });
84
+ }
85
+ }
86
+
87
+ const legacy = keys.some((key) => !NATIVE_KEYS.has(key)) || dropped.length > 0;
88
+ return { legacy, dropped };
89
+ }
90
+
91
+ const isConvertibleKey = (key) => (
92
+ key === 'prompt'
93
+ || key === 'tools'
94
+ || key === 'disable'
95
+ || key === 'maxSteps'
96
+ || key === 'permission'
97
+ || key === 'name'
98
+ || key === 'options'
99
+ );
100
+
101
+ /**
102
+ * @param {unknown} config
103
+ * @returns {{ frontmatter: Record<string, unknown>, body: string, legacy: boolean, dropped: Array<{ key: string, reason: string }> }}
104
+ */
105
+ export function convertAgentConfig(config) {
106
+ const source = config && typeof config === 'object' && !Array.isArray(config) ? { ...config } : {};
107
+ const inspection = inspectAgentConfig(source);
108
+ /** @type {Record<string, unknown>} */
109
+ const frontmatter = {};
110
+
111
+ if (typeof source.description === 'string' && source.description.trim()) {
112
+ frontmatter.description = source.description;
113
+ }
114
+ if (source.mode === 'primary' || source.mode === 'subagent' || source.mode === 'all') {
115
+ frontmatter.mode = source.mode;
116
+ }
117
+
118
+ const model = encodeModel(source.model, source.variant);
119
+ if (model) frontmatter.model = model;
120
+
121
+ if (source.hidden === true) frontmatter.hidden = true;
122
+ if (source.disabled === true || source.disable === true) frontmatter.disabled = true;
123
+ if (typeof source.color === 'string' && HEX_COLOR.test(source.color)) frontmatter.color = source.color;
124
+
125
+ const steps = positiveInt(source.steps) ?? positiveInt(source.maxSteps);
126
+ if (steps !== undefined) frontmatter.steps = steps;
127
+
128
+ if (isRequest(source.request)) frontmatter.request = source.request;
129
+
130
+ const permissions = normalizePermissions(source.permissions ?? source.permission, source.tools);
131
+ if (permissions.length > 0) frontmatter.permissions = permissions;
132
+
133
+ const body = readSystem(source);
134
+ return { frontmatter, body, legacy: inspection.legacy, dropped: inspection.dropped };
135
+ }
136
+
137
+ /**
138
+ * @param {{ frontmatter: Record<string, unknown>, body: string }} current
139
+ * @param {Record<string, unknown>} patch
140
+ */
141
+ export function applyNativePatch(current, patch) {
142
+ const frontmatter = { ...current.frontmatter };
143
+ let body = current.body;
144
+ const changes = patch && typeof patch === 'object' ? patch : {};
145
+ for (const [key, value] of Object.entries(changes)) {
146
+ if (value === undefined || key === 'name' || key === 'scope' || key === 'confirmDrop') continue;
147
+ if (key === 'system') {
148
+ body = value === null ? '' : String(value);
149
+ delete frontmatter.system;
150
+ continue;
151
+ }
152
+ if (value === null || value === false && (key === 'hidden' || key === 'disabled')) {
153
+ delete frontmatter[key];
154
+ if (key === 'model') delete frontmatter.variant;
155
+ continue;
156
+ }
157
+ if (key === 'color' && (typeof value !== 'string' || !HEX_COLOR.test(value))) {
158
+ continue;
159
+ }
160
+ if (key === 'steps') {
161
+ const steps = positiveInt(value);
162
+ if (steps === undefined) delete frontmatter.steps;
163
+ else frontmatter.steps = steps;
164
+ continue;
165
+ }
166
+ if (key === 'permissions') {
167
+ const permissions = normalizePermissions(value, undefined);
168
+ delete frontmatter.permission;
169
+ if (permissions.length === 0) delete frontmatter.permissions;
170
+ else frontmatter.permissions = permissions;
171
+ continue;
172
+ }
173
+ if (key === 'model') delete frontmatter.variant;
174
+ frontmatter[key] = value;
175
+ }
176
+ return { frontmatter, body };
177
+ }
178
+
179
+ /**
180
+ * Write only the keys in `patch` onto the existing markdown document.
181
+ * `confirmDrop` removes fields the current format cannot represent.
182
+ * Unmentioned fields, including permissions, stay as they are.
183
+ *
184
+ * @param {{ frontmatter?: Record<string, unknown>, body?: string }} existing
185
+ * @param {Record<string, unknown>} patch
186
+ * @param {boolean} confirmDrop
187
+ */
188
+ export function writeAgentDocument(existing, patch, confirmDrop) {
189
+ const frontmatter = { ...(existing?.frontmatter || {}) };
190
+ let body = typeof existing?.body === 'string' ? existing.body : '';
191
+ if (confirmDrop) {
192
+ const inspection = inspectAgentConfig({
193
+ ...frontmatter,
194
+ ...(body.trim() ? { prompt: body } : {}),
195
+ });
196
+ for (const item of inspection.dropped) {
197
+ if (item.key === 'prompt') {
198
+ body = '';
199
+ delete frontmatter.prompt;
200
+ } else {
201
+ delete frontmatter[item.key];
202
+ }
203
+ }
204
+ }
205
+ return applyNativePatch({ frontmatter, body }, patch);
206
+ }
207
+
208
+ const encodeModel = (model, variant) => {
209
+ if (typeof model !== 'string') return undefined;
210
+ const trimmed = model.trim();
211
+ if (!trimmed) return undefined;
212
+ if (trimmed.includes('#')) return trimmed;
213
+ if (typeof variant === 'string' && variant.trim() && !variant.includes('#')) {
214
+ return `${trimmed}#${variant.trim()}`;
215
+ }
216
+ return trimmed;
217
+ };
218
+
219
+ const positiveInt = (value) => {
220
+ const number = typeof value === 'number' ? value : typeof value === 'string' && value.trim() ? Number(value) : NaN;
221
+ if (!Number.isInteger(number) || number < 1) return undefined;
222
+ return number;
223
+ };
224
+
225
+ const isRequest = (value) => {
226
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
227
+ const request = /** @type {Record<string, unknown>} */ (value);
228
+ return request.headers != null || request.body != null;
229
+ };
230
+
231
+ const readSystem = (source) => {
232
+ if (typeof source.system === 'string') return source.system;
233
+ if (typeof source.prompt === 'string' && !PROMPT_FILE.test(source.prompt.trim())) return source.prompt;
234
+ return '';
235
+ };
236
+
237
+ const normalizePermissions = (permission, tools) => {
238
+ /** @type {Array<{ action: string, resource: string, effect: string }>} */
239
+ const rules = [];
240
+ if (tools && typeof tools === 'object' && !Array.isArray(tools)) {
241
+ for (const [tool, enabled] of Object.entries(tools)) {
242
+ if (typeof enabled !== 'boolean') continue;
243
+ pushRule(rules, renameAction(tool), '*', enabled ? 'allow' : 'deny');
244
+ }
245
+ }
246
+ if (isEffect(permission)) {
247
+ pushRule(rules, '*', '*', permission);
248
+ } else if (Array.isArray(permission)) {
249
+ for (const rule of permission) {
250
+ if (!rule || typeof rule !== 'object') continue;
251
+ if (typeof rule.permission === 'string' && typeof rule.pattern === 'string') {
252
+ pushRule(rules, renameAction(rule.permission), rule.pattern, rule.action);
253
+ continue;
254
+ }
255
+ pushRule(rules, renameAction(rule.action), rule.resource, rule.effect);
256
+ }
257
+ } else if (permission && typeof permission === 'object') {
258
+ for (const [key, rule] of Object.entries(permission)) {
259
+ const action = renameAction(key);
260
+ if (isEffect(rule)) {
261
+ pushRule(rules, action, '*', rule);
262
+ continue;
263
+ }
264
+ if (rule && typeof rule === 'object' && !Array.isArray(rule)) {
265
+ for (const [resource, effect] of Object.entries(rule)) {
266
+ pushRule(rules, action, resource, effect);
267
+ }
268
+ }
269
+ }
270
+ }
271
+ return rules;
272
+ };
273
+
274
+ const pushRule = (rules, action, resource, effect) => {
275
+ if (!action || !resource || !isEffect(effect)) return;
276
+ const index = rules.findIndex((rule) => rule.action === action && rule.resource === resource);
277
+ const next = { action, resource, effect };
278
+ if (index >= 0) rules[index] = next;
279
+ else rules.push(next);
280
+ };