@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
package/server/index.js CHANGED
@@ -39,6 +39,7 @@ import { resolveOpenCodeEnvConfig } from './lib/opencode/env-config.js';
39
39
  import { createHmrStateRuntime } from './lib/opencode/hmr-state-runtime.js';
40
40
  import { createOpenCodeNetworkRuntime } from './lib/opencode/network-runtime.js';
41
41
  import { createOpenCodeAuthStateRuntime } from './lib/opencode/auth-state-runtime.js';
42
+ import { createSharedOpenCodeService, isSharedServiceEnabled } from './lib/opencode/shared-service.js';
42
43
  import { createProjectDirectoryRuntime } from './lib/opencode/project-directory-runtime.js';
43
44
  import { createSettingsNormalizationRuntime } from './lib/opencode/settings-normalization-runtime.js';
44
45
  import { createSettingsHelpers } from './lib/opencode/settings-helpers.js';
@@ -52,6 +53,10 @@ import {
52
53
  } from './lib/opencode/core-routes.js';
53
54
  import { registerOpenChamberRoutes } from './lib/opencode/openchamber-routes.js';
54
55
  import { createServerUtilsRuntime } from './lib/opencode/server-utils-runtime.js';
56
+ import {
57
+ configureServerOpenCodeFetchGate,
58
+ createServerOpenCodeFetch,
59
+ } from './lib/opencode/server-opencode-fetch.js';
55
60
  import { createStaticRoutesRuntime } from './lib/opencode/static-routes-runtime.js';
56
61
  import { createSettingsRuntime } from './lib/opencode/settings-runtime.js';
57
62
  import { createOpenCodeResolutionRuntime } from './lib/opencode/opencode-resolution-runtime.js';
@@ -59,6 +64,7 @@ import { createBootstrapRuntime } from './lib/opencode/bootstrap-runtime.js';
59
64
  import { createSessionRuntime } from './lib/opencode/session-runtime.js';
60
65
  import { createOpenCodeWatcherRuntime } from './lib/opencode/watcher.js';
61
66
  import { createSessionTitleRuntime } from './lib/session-title/runtime.js';
67
+ import { createTitleSessionAccess } from './lib/session-title/session-access.js';
62
68
  import { createSessionIndexService } from './lib/session-index/service.js';
63
69
  import { createSessionIndexSyncRuntime } from './lib/session-index/sync-runtime.js';
64
70
  import { resolveSessionIndexDbPath } from './lib/session-index/resolve-db-path.js';
@@ -68,7 +74,20 @@ import { resolveTranscriptCacheDbPath } from './lib/transcript-cache/resolve-db-
68
74
  import { createMessageQueueRuntime } from './lib/message-queue/runtime.js';
69
75
  import { resolveMessageQueueDbPath } from './lib/message-queue/resolve-db-path.js';
70
76
  import { createOpenChamberEventBroadcaster } from './lib/opencode/feature-routes-runtime.js';
71
- import { createSessionGoalRuntime } from './lib/session-goal/runtime.js';
77
+ import {
78
+ createSessionGoalRuntime,
79
+ withGoalExecutionGeneration,
80
+ } from './lib/session-goal/runtime.js';
81
+ import { createSessionMetadataStore } from './lib/session-metadata/session-metadata-store.js';
82
+ import {
83
+ createSessionArchiveService,
84
+ createUpstreamSessionFetcher,
85
+ } from './lib/session-metadata/session-archive.js';
86
+ import {
87
+ projectSessionLifecyclePayload,
88
+ projectSessionWithHostMetadata,
89
+ projectSessionWithStoredMap,
90
+ } from './lib/session-metadata/session-projection.js';
72
91
  import { createScheduledTasksRuntime } from './lib/scheduled-tasks/runtime.js';
73
92
  import { createScheduledTaskRunHistoryStore } from './lib/scheduled-tasks/run-history-store.js';
74
93
  import { createServerStartupRuntime } from './lib/opencode/server-startup-runtime.js';
@@ -88,6 +107,7 @@ import { createNotificationTemplateRuntime } from './lib/notifications/template-
88
107
  import { createPermissionAutoAcceptRuntime } from './lib/permission-auto-accept/runtime.js';
89
108
  import { createQuestionAutoDelegateRuntime } from './lib/question-auto-delegate/runtime.js';
90
109
  import { createGracefulShutdownRuntime } from './lib/opencode/shutdown-runtime.js';
110
+ import { attachHttpServerConnectionTracker } from './lib/opencode/http-server-connections.js';
91
111
  import { createProjectConfigRuntime } from './lib/projects/project-config.js';
92
112
  import { createRemoteClientAuthRuntime } from './lib/client-auth/remote-clients.js';
93
113
  import { applyRuntimeCorsHeaders } from './lib/request-cors.js';
@@ -445,6 +465,12 @@ let openCodeApiDetectionTimer = null;
445
465
  let lastOpenCodeError = null;
446
466
  let lastOpenCodeLaunchDiagnostics = null;
447
467
  let isOpenCodeReady = false;
468
+ let v1Migration = null;
469
+ let openCodeServeVersion = null;
470
+ let openCodeCliVersion = null;
471
+ let runtimeContract = null;
472
+ /** Bumps on start/restart so stale async health cannot re-permit an old instance. */
473
+ let runtimeContractGeneration = 0;
448
474
  let openCodeNotReadySince = 0;
449
475
  let isExternalOpenCode = false;
450
476
  let exitOnShutdown = true;
@@ -549,6 +575,7 @@ const openCodeAuthStateRuntime = createOpenCodeAuthStateRuntime({
549
575
  const getOpenCodeAuthHeaders = (...args) => openCodeAuthStateRuntime.getOpenCodeAuthHeaders(...args);
550
576
  const isOpenCodeConnectionSecure = (...args) => openCodeAuthStateRuntime.isOpenCodeConnectionSecure(...args);
551
577
  const ensureLocalOpenCodeServerPassword = (...args) => openCodeAuthStateRuntime.ensureLocalOpenCodeServerPassword(...args);
578
+ const adoptOpenCodeServerPassword = (...args) => openCodeAuthStateRuntime.adoptOpenCodeServerPassword(...args);
552
579
 
553
580
  const openCodeNetworkState = {};
554
581
  Object.defineProperties(openCodeNetworkState, {
@@ -572,6 +599,15 @@ const buildOpenCodeUrl = (...args) => openCodeNetworkRuntime.buildOpenCodeUrl(..
572
599
  const ensureOpenCodeApiPrefix = (...args) => openCodeNetworkRuntime.ensureOpenCodeApiPrefix(...args);
573
600
  const scheduleOpenCodeApiDetection = (...args) => openCodeNetworkRuntime.scheduleOpenCodeApiDetection(...args);
574
601
 
602
+ // Host OpenCode transport gate (queue/goal/scheduled/assistants + v2-client).
603
+ // Registered before background runtimes so direct OpenCode writes share proxy admission.
604
+ configureServerOpenCodeFetchGate(() => runtimeContract);
605
+ const serverOpenCodeFetch = createServerOpenCodeFetch({
606
+ buildOpenCodeUrl,
607
+ getOpenCodeAuthHeaders,
608
+ getRuntimeContract: () => runtimeContract,
609
+ });
610
+
575
611
  const ENV_CONFIGURED_API_PREFIX = normalizeApiPrefix(
576
612
  process.env.OPENCODE_API_PREFIX || process.env.OPENCHAMBER_API_PREFIX || ''
577
613
  );
@@ -614,6 +650,7 @@ const openCodeEnvRuntime = createOpenCodeEnvRuntime({
614
650
  const applyLoginShellEnvSnapshot = (...args) => openCodeEnvRuntime.applyLoginShellEnvSnapshot(...args);
615
651
  const getLoginShellEnvSnapshot = (...args) => openCodeEnvRuntime.getLoginShellEnvSnapshot(...args);
616
652
  const ensureOpencodeCliEnv = (...args) => openCodeEnvRuntime.ensureOpencodeCliEnv(...args);
653
+ const ensurePinnedOpenCode2CliEnv = (...args) => openCodeEnvRuntime.ensurePinnedOpenCode2CliEnv(...args);
617
654
  const applyOpencodeBinaryFromSettings = (...args) => openCodeEnvRuntime.applyOpencodeBinaryFromSettings(...args);
618
655
  const resolveOpencodeCliPath = (...args) => openCodeEnvRuntime.resolveOpencodeCliPath(...args);
619
656
  const isExecutable = (...args) => openCodeEnvRuntime.isExecutable(...args);
@@ -621,6 +658,7 @@ const searchPathFor = (...args) => openCodeEnvRuntime.searchPathFor(...args);
621
658
  const resolveGitBinaryForSpawn = (...args) => openCodeEnvRuntime.resolveGitBinaryForSpawn(...args);
622
659
  const resolveManagedOpenCodeLaunchSpec = (...args) => openCodeEnvRuntime.resolveManagedOpenCodeLaunchSpec(...args);
623
660
  const clearResolvedOpenCodeBinary = (...args) => openCodeEnvRuntime.clearResolvedOpenCodeBinary(...args);
661
+ const forceResolvedOpenCodeBinary = (...args) => openCodeEnvRuntime.forceResolvedOpenCodeBinary(...args);
624
662
  const openCodeResolutionRuntime = createOpenCodeResolutionRuntime({
625
663
  path,
626
664
  resolveOpencodeCliPath,
@@ -696,6 +734,8 @@ const getSmallModelService = async () => {
696
734
  smallModelServiceInstance = createSmallModelService({
697
735
  buildOpenCodeUrl,
698
736
  getOpenCodeAuthHeaders,
737
+ persistSessionMetadata: persistSessionMetadataToStore,
738
+ onSystemSessionPersisted: hideSystemSessionFromIndex,
699
739
  });
700
740
  }
701
741
  return smallModelServiceInstance;
@@ -705,6 +745,19 @@ const sessionTitleRuntime = createSessionTitleRuntime({
705
745
  buildOpenCodeUrl,
706
746
  getOpenCodeAuthHeaders,
707
747
  getSmallModelService,
748
+ serverOpenCodeFetch,
749
+ sessionAccess: createTitleSessionAccess({
750
+ buildOpenCodeUrl,
751
+ getOpenCodeAuthHeaders,
752
+ readSessionMetadata: (sessionID) => sessionMetadataStore.get(sessionID),
753
+ persistSessionMetadata: (sessionID, patch) => persistSessionMetadataToStore(sessionID, patch),
754
+ publishSession: (session, directory) => {
755
+ const payload = { type: 'session.updated', properties: { info: session, directory } };
756
+ broadcastGlobalUiEvent(payload);
757
+ const changed = sessionIndexServiceRef?.upsertAndReportChange(session);
758
+ if (changed) sessionIndexSyncRuntimeRef?.publishChange();
759
+ },
760
+ }),
708
761
  });
709
762
 
710
763
  // Forward declaration: question auto-delegate is created after the event hub;
@@ -713,9 +766,116 @@ let questionAutoDelegateRuntime = null;
713
766
  /** Filled inside main() once the SQLite session index is constructed. */
714
767
  let sessionIndexServiceRef = null;
715
768
 
769
+ const sessionMetadataStore = createSessionMetadataStore({ dataDir: OPENCHAMBER_DATA_DIR });
770
+ /**
771
+ * load() resolves `{ ok:false }` on read/corrupt failure (does not reject).
772
+ * Callers must check ok / isLoaded — never treat unresolved as empty success.
773
+ */
774
+ const ensureSessionMetadataReady = async () => {
775
+ try {
776
+ const result = await sessionMetadataStore.load();
777
+ return result?.ok === true && sessionMetadataStore.isLoaded();
778
+ } catch (error) {
779
+ console.warn('[session-metadata] load threw:', error?.message ?? error);
780
+ return false;
781
+ }
782
+ };
783
+ // Prime without blocking module init; production gates (hub/SSE/routes) await ready.
784
+ void ensureSessionMetadataReady().then((ok) => {
785
+ if (!ok) {
786
+ console.warn(
787
+ '[session-metadata] initial load not ready:',
788
+ sessionMetadataStore.getLoadFailureReason?.() || 'unknown',
789
+ );
790
+ }
791
+ });
792
+
793
+ /**
794
+ * Goal write through the Host metadata store.
795
+ * Advances executionGeneration on pause/resume/condition changes so late audits
796
+ * cannot commit against a prior execution permit. Broadcasts only after commit.
797
+ */
798
+ const persistSessionGoalToStore = async (sessionId, _directory, goal) => {
799
+ const result = await sessionMetadataStore.mutateSessionMetadata(sessionId, (current) => {
800
+ const previousGoal = current?.openchamber?.goal && typeof current.openchamber.goal === 'object'
801
+ ? current.openchamber.goal
802
+ : null;
803
+ const nextGoal = withGoalExecutionGeneration(previousGoal, goal);
804
+ return { ok: true, patch: { openchamber: { goal: nextGoal } } };
805
+ });
806
+ if (!result?.committed) {
807
+ throw new Error(result?.reason || 'session goal persist was rejected');
808
+ }
809
+ const metadata = result.metadata;
810
+ try {
811
+ broadcastGlobalUiEvent({
812
+ type: 'openchamber:session-metadata',
813
+ properties: { sessionID: sessionId, metadata },
814
+ });
815
+ } catch (error) {
816
+ console.warn('[session-metadata] broadcast failed:', error?.message ?? error);
817
+ }
818
+ return metadata;
819
+ };
820
+
821
+ /**
822
+ * Isolation / Host metadata write. Same store + broadcast seam as goal persist.
823
+ * Callers pass an RFC 7386 merge patch; the store returns the committed object.
824
+ */
825
+ const persistSessionMetadataToStore = async (sessionId, patch) => {
826
+ const metadata = await sessionMetadataStore.setSessionMetadata(sessionId, patch);
827
+ try {
828
+ broadcastGlobalUiEvent({
829
+ type: 'openchamber:session-metadata',
830
+ properties: { sessionID: sessionId, metadata },
831
+ });
832
+ } catch (error) {
833
+ console.warn('[session-metadata] broadcast failed:', error?.message ?? error);
834
+ }
835
+ return metadata;
836
+ };
837
+
838
+ /**
839
+ * After Host isolation metadata is committed, drop the session from the
840
+ * sidebar index immediately. Do not wait for the next directory list sync.
841
+ * Reads refs at call time — scheduled-task runtime is constructed before main().
842
+ */
843
+ const hideSystemSessionFromIndex = ({ sessionID, directory, metadata } = {}) => {
844
+ if (!sessionIndexServiceRef || typeof sessionID !== 'string' || !sessionID) return;
845
+ const changed = sessionIndexServiceRef.upsertAndReportChange({
846
+ id: sessionID,
847
+ directory: directory || '',
848
+ metadata,
849
+ });
850
+ if (changed) {
851
+ sessionIndexSyncRuntimeRef?.publishChange();
852
+ }
853
+ };
854
+
855
+ /** Filled in main() once the session index + broadcast sinks exist. */
856
+ let sessionArchiveServiceRef = null;
857
+ let sessionIndexSyncRuntimeRef = null;
858
+
859
+ const readHostMetadataForSession = (sessionID) => {
860
+ const snap = sessionMetadataStore.getSnapshotSync();
861
+ if (!snap || typeof sessionID !== 'string') return null;
862
+ const host = snap[sessionID];
863
+ return host && typeof host === 'object' && !Array.isArray(host) ? host : null;
864
+ };
865
+
866
+ const projectHostSessionPayload = (payload) => {
867
+ const ready = sessionMetadataStore.isLoaded();
868
+ return projectSessionLifecyclePayload(
869
+ payload,
870
+ readHostMetadataForSession,
871
+ { hostReady: ready },
872
+ );
873
+ };
874
+
716
875
  const sessionGoalRuntime = createSessionGoalRuntime({
717
876
  buildOpenCodeUrl,
718
877
  getOpenCodeAuthHeaders,
878
+ serverOpenCodeFetch,
719
879
  getSmallModelService,
720
880
  shouldKeepGoalActiveForQuestion: (sessionId) => (
721
881
  questionAutoDelegateRuntime?.isAutoHandling(sessionId) === true
@@ -726,6 +886,28 @@ const sessionGoalRuntime = createSessionGoalRuntime({
726
886
  onGoalPaused: (sessionId, directory) => (
727
887
  questionAutoDelegateRuntime?.pauseForSessionTree?.(sessionId, directory)
728
888
  ),
889
+ readSessionMetadata: (sessionId) => sessionMetadataStore.get(sessionId),
890
+ // Conditional commit on the store's exclusive serial boundary (generation +
891
+ // status preconditions share the lock with persist). Broadcast-only on
892
+ // success — do not feed progress writes back into processPayload.
893
+ mutateSessionMetadata: async (sessionId, decide) => {
894
+ const result = await sessionMetadataStore.mutateSessionMetadata(sessionId, decide);
895
+ if (result?.committed) {
896
+ try {
897
+ broadcastGlobalUiEvent({
898
+ type: 'openchamber:session-metadata',
899
+ properties: { sessionID: sessionId, metadata: result.metadata },
900
+ });
901
+ } catch (error) {
902
+ console.warn('[session-metadata] broadcast failed:', error?.message ?? error);
903
+ }
904
+ }
905
+ return result;
906
+ },
907
+ persistSessionGoal: async (sessionId, directory, goal) => {
908
+ const metadata = await persistSessionGoalToStore(sessionId, directory, goal);
909
+ return metadata?.openchamber?.goal ?? goal;
910
+ },
729
911
  emitGoalNotification: async ({ sessionId, directory, status, goal }) => {
730
912
  // The goal settle notification replaces the per-turn ready notifications
731
913
  // (suppressed while the goal is active) — so it obeys the same toggle.
@@ -766,6 +948,7 @@ const globalMessageStreamHub = createGlobalMessageStreamHub({
766
948
  buildOpenCodeUrl,
767
949
  getOpenCodeAuthHeaders,
768
950
  upstreamStallTimeoutMs: getUpstreamStallTimeoutMs,
951
+ projectOutboundSessionPayload: projectHostSessionPayload,
769
952
  });
770
953
 
771
954
  const permissionAutoAcceptRuntime = createPermissionAutoAcceptRuntime({
@@ -908,6 +1091,9 @@ const serverUtilsRuntime = createServerUtilsRuntime({
908
1091
  openCodeNotReadySince,
909
1092
  isOpenCodeReady,
910
1093
  isRestartingOpenCode,
1094
+ v1Migration,
1095
+ runtimeContract,
1096
+ openCodeServeVersion,
911
1097
  }),
912
1098
  getOpenCodeAuthHeaders,
913
1099
  buildOpenCodeUrl,
@@ -934,6 +1120,13 @@ const serverUtilsRuntime = createServerUtilsRuntime({
934
1120
  }
935
1121
  return snapshot.PATH;
936
1122
  },
1123
+ getStoredSessionMetadata: () => sessionMetadataStore.getAll(),
1124
+ getStoredSessionMetadataSync: () => sessionMetadataStore.getSnapshotSync(),
1125
+ ensureSessionMetadataReady,
1126
+ onSessionDeleted: (sessionID) => {
1127
+ if (!sessionArchiveServiceRef) return sessionMetadataStore.removeSession(sessionID);
1128
+ return sessionArchiveServiceRef.forgetSession(sessionID);
1129
+ },
937
1130
  });
938
1131
 
939
1132
  const setOpenCodePort = (...args) => serverUtilsRuntime.setOpenCodePort(...args);
@@ -1003,6 +1196,14 @@ Object.defineProperties(openCodeLifecycleState, {
1003
1196
  lastOpenCodeError: { get: () => lastOpenCodeError, set: (value) => { lastOpenCodeError = value; } },
1004
1197
  lastOpenCodeLaunchDiagnostics: { get: () => lastOpenCodeLaunchDiagnostics, set: (value) => { lastOpenCodeLaunchDiagnostics = value; } },
1005
1198
  isOpenCodeReady: { get: () => isOpenCodeReady, set: (value) => { isOpenCodeReady = value; } },
1199
+ v1Migration: { get: () => v1Migration, set: (value) => { v1Migration = value; } },
1200
+ openCodeServeVersion: { get: () => openCodeServeVersion, set: (value) => { openCodeServeVersion = value; } },
1201
+ openCodeCliVersion: { get: () => openCodeCliVersion, set: (value) => { openCodeCliVersion = value; } },
1202
+ runtimeContract: { get: () => runtimeContract, set: (value) => { runtimeContract = value; } },
1203
+ runtimeContractGeneration: {
1204
+ get: () => runtimeContractGeneration,
1205
+ set: (value) => { runtimeContractGeneration = value; },
1206
+ },
1006
1207
  openCodeNotReadySince: { get: () => openCodeNotReadySince, set: (value) => { openCodeNotReadySince = value; } },
1007
1208
  isExternalOpenCode: { get: () => isExternalOpenCode, set: (value) => { isExternalOpenCode = value; } },
1008
1209
  isShuttingDown: { get: () => isShuttingDown, set: (value) => { isShuttingDown = value; } },
@@ -1047,7 +1248,10 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
1047
1248
  normalizeApiPrefix,
1048
1249
  applyOpencodeBinaryFromSettings,
1049
1250
  ensureOpencodeCliEnv,
1251
+ ensurePinnedOpenCode2CliEnv,
1050
1252
  ensureLocalOpenCodeServerPassword,
1253
+ adoptOpenCodeServerPassword,
1254
+ sharedService: isSharedServiceEnabled() ? createSharedOpenCodeService() : null,
1051
1255
  resolveManagedOpenCodeLaunchSpec,
1052
1256
  setOpenCodePort,
1053
1257
  setDetectedOpenCodeApiPrefix,
@@ -1059,6 +1263,26 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
1059
1263
  getManagedOpenCodeShellEnvSnapshot: getLoginShellEnvSnapshot,
1060
1264
  managedCapabilitiesRuntime,
1061
1265
  getActiveSessionCount,
1266
+ // Most-recently-used directories first: OpenCode initializes each directory
1267
+ // lazily on first request (seconds on large session stores), so the
1268
+ // lifecycle warms these right after readiness — before the UI's first
1269
+ // interactive request would otherwise pay that cost.
1270
+ getWarmupDirectories: async () => {
1271
+ const settings = await readSettingsFromDiskMigrated().catch(() => null);
1272
+ if (!settings) return [];
1273
+ const directories = [];
1274
+ if (typeof settings.lastDirectory === 'string' && settings.lastDirectory) {
1275
+ directories.push(settings.lastDirectory);
1276
+ }
1277
+ const projects = Array.isArray(settings.projects) ? [...settings.projects] : [];
1278
+ projects.sort((a, b) => (b?.lastOpenedAt ?? 0) - (a?.lastOpenedAt ?? 0));
1279
+ for (const project of projects) {
1280
+ if (typeof project?.path === 'string' && project.path) {
1281
+ directories.push(project.path);
1282
+ }
1283
+ }
1284
+ return [...new Set(directories)];
1285
+ },
1062
1286
  });
1063
1287
 
1064
1288
  const restartOpenCode = (...args) => openCodeLifecycleRuntime.restartOpenCode(...args);
@@ -1081,6 +1305,25 @@ const scheduledTasksRuntime = createScheduledTasksRuntime({
1081
1305
  getSmallModelService,
1082
1306
  waitForOpenCodeReady,
1083
1307
  runHistoryStore: scheduledTaskRunHistoryStore,
1308
+ // Same Host store seam as session-goal / manual UI metadata writes.
1309
+ readSessionMetadata: (sessionId) => sessionMetadataStore.get(sessionId),
1310
+ persistSessionMetadata: persistSessionMetadataToStore,
1311
+ onSystemSessionPersisted: hideSystemSessionFromIndex,
1312
+ persistSessionGoal: persistSessionGoalToStore,
1313
+ // Arm the goal loop after first persist only — do not feed progress writes
1314
+ // back through processPayload (session-goal runtime already broadcasts those).
1315
+ onGoalPersisted: ({ sessionID, directory, metadata }) => {
1316
+ sessionGoalRuntime.processPayload({
1317
+ type: 'session.updated',
1318
+ properties: {
1319
+ info: {
1320
+ id: sessionID,
1321
+ directory: directory || '',
1322
+ metadata,
1323
+ },
1324
+ },
1325
+ }, directory || '');
1326
+ },
1084
1327
  emitTaskRunEvent: (event) => {
1085
1328
  for (const client of uiOpenChamberEventClients) {
1086
1329
  try {
@@ -1108,7 +1351,19 @@ const ensureGlobalWatcherStarted = async () => {
1108
1351
  return globalWatcherStartPromise;
1109
1352
  }
1110
1353
 
1111
- globalWatcherStartPromise = openCodeWatcherRuntime.start().catch((error) => {
1354
+ // Await Host metadata ready before the internal hub attaches upstream SSE so
1355
+ // session lifecycle frames are projected (not dropped) from the first event.
1356
+ globalWatcherStartPromise = (async () => {
1357
+ const ready = await ensureSessionMetadataReady();
1358
+ if (!ready) {
1359
+ const reason = sessionMetadataStore.getLoadFailureReason?.() || 'not ready';
1360
+ const error = new Error(`session metadata unavailable before global watcher start: ${reason}`);
1361
+ error.code = 'session_metadata_unavailable';
1362
+ error.retryable = true;
1363
+ throw error;
1364
+ }
1365
+ return openCodeWatcherRuntime.start();
1366
+ })().catch((error) => {
1112
1367
  globalWatcherStartPromise = null;
1113
1368
  throw error;
1114
1369
  });
@@ -1118,15 +1373,30 @@ const ensureGlobalWatcherStarted = async () => {
1118
1373
 
1119
1374
  const completeOpenCodeStartup = () => {
1120
1375
  scheduleOpenCodeApiDetection();
1121
- if (openCodeLifecycleState.openCodeProcess && !openCodeLifecycleState.isExternalOpenCode) {
1376
+ if (
1377
+ (openCodeLifecycleState.openCodeProcess && !openCodeLifecycleState.isExternalOpenCode)
1378
+ || openCodeLifecycleRuntime.isSharedOpenCodeService()
1379
+ ) {
1122
1380
  startHealthMonitoring();
1123
1381
  }
1124
1382
  // The global watcher used to start only for desktop notifications; session-title
1125
1383
  // and session-goal also ride its event hub, so it now starts unconditionally
1126
- // once OpenCode is up.
1127
- void ensureGlobalWatcherStarted().catch((error) => {
1128
- console.warn(`Global event watcher startup failed: ${error?.message || error}`);
1129
- });
1384
+ // once OpenCode is up. Controlled retry when metadata is not yet ready.
1385
+ const startWatcherWithRetry = (attempt = 0) => {
1386
+ void ensureGlobalWatcherStarted().catch((error) => {
1387
+ const retryable = error?.retryable === true || error?.code === 'session_metadata_unavailable';
1388
+ const delays = [500, 1_500, 3_000, 6_000, 12_000];
1389
+ if (retryable && attempt < delays.length) {
1390
+ console.warn(
1391
+ `Global event watcher deferred (metadata): ${error?.message || error}; retry ${attempt + 1}/${delays.length}`,
1392
+ );
1393
+ setTimeout(() => startWatcherWithRetry(attempt + 1), delays[attempt]);
1394
+ return;
1395
+ }
1396
+ console.warn(`Global event watcher startup failed: ${error?.message || error}`);
1397
+ });
1398
+ };
1399
+ startWatcherWithRetry();
1130
1400
  };
1131
1401
 
1132
1402
  const bootstrapOpenCodeAtStartup = async (...args) => {
@@ -1138,7 +1408,6 @@ const retryOpenCodeStartup = async (...args) => {
1138
1408
  await openCodeLifecycleRuntime.retryOpenCodeStartup(...args);
1139
1409
  completeOpenCodeStartup();
1140
1410
  };
1141
- const killProcessOnPort = (...args) => openCodeLifecycleRuntime.killProcessOnPort(...args);
1142
1411
  const waitForPortRelease = (...args) => openCodeLifecycleRuntime.waitForPortRelease(...args);
1143
1412
 
1144
1413
  const fetchAgentsSnapshot = (...args) => serverUtilsRuntime.fetchAgentsSnapshot(...args);
@@ -1175,7 +1444,6 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
1175
1444
  setOpenCodeProcess: (value) => {
1176
1445
  openCodeProcess = value;
1177
1446
  },
1178
- killProcessOnPort,
1179
1447
  waitForPortRelease,
1180
1448
  getServer: () => server,
1181
1449
  getUiAuthController: () => uiAuthController,
@@ -1350,6 +1618,14 @@ async function main(options = {}) {
1350
1618
  buildOpenCodeUrl,
1351
1619
  getOpenCodeAuthHeaders,
1352
1620
  waitForOpenCodeReady,
1621
+ projectSessions: (sessions) => {
1622
+ const snap = sessionMetadataStore.getSnapshotSync();
1623
+ // Not ready / unavailable — throw so sync keeps prior directory rows.
1624
+ if (!snap) {
1625
+ throw new Error('session metadata is unavailable: Host store not ready');
1626
+ }
1627
+ return sessions.map((session) => projectSessionWithStoredMap(session, snap));
1628
+ },
1353
1629
  onRevisionTip: (tip) => {
1354
1630
  broadcastOpenChamberEvent({
1355
1631
  type: 'openchamber:session-index-changed',
@@ -1357,8 +1633,44 @@ async function main(options = {}) {
1357
1633
  });
1358
1634
  },
1359
1635
  });
1636
+ sessionIndexSyncRuntimeRef = sessionIndexSyncRuntime;
1637
+
1638
+ sessionArchiveServiceRef = createSessionArchiveService({
1639
+ sessionMetadataStore,
1640
+ fetchUpstreamSession: createUpstreamSessionFetcher({
1641
+ buildOpenCodeUrl,
1642
+ getOpenCodeAuthHeaders,
1643
+ }),
1644
+ sessionIndexService,
1645
+ broadcastSessionEvent: (payload, options = {}) => {
1646
+ try {
1647
+ broadcastGlobalUiEvent(payload, options);
1648
+ } catch (error) {
1649
+ console.warn('[session-archive] global UI broadcast failed:', error?.message ?? error);
1650
+ }
1651
+ try {
1652
+ broadcastOpenChamberEvent(payload);
1653
+ } catch (error) {
1654
+ console.warn('[session-archive] openchamber event broadcast failed:', error?.message ?? error);
1655
+ }
1656
+ },
1657
+ onIndexChanged: () => {
1658
+ sessionIndexSyncRuntimeRef?.publishChange();
1659
+ },
1660
+ });
1661
+
1360
1662
  const unsubscribeSessionIndexEvents = globalMessageStreamHub.subscribeEvent((event) => {
1361
- if (applySessionIndexEvent(sessionIndexService, event)) {
1663
+ if (applySessionIndexEvent(sessionIndexService, event, Date.now(), {
1664
+ isHostReady: () => sessionMetadataStore.isLoaded(),
1665
+ projectSession: (session) => {
1666
+ if (!sessionMetadataStore.isLoaded()) return null;
1667
+ return projectSessionWithHostMetadata(
1668
+ session,
1669
+ readHostMetadataForSession(session?.id),
1670
+ );
1671
+ },
1672
+ onSessionDeleted: (sessionID) => sessionArchiveServiceRef?.forgetSession(sessionID),
1673
+ })) {
1362
1674
  sessionIndexSyncRuntime?.publishChange();
1363
1675
  }
1364
1676
  });
@@ -1424,6 +1736,9 @@ async function main(options = {}) {
1424
1736
  }));
1425
1737
  expressApp = app;
1426
1738
  server = http.createServer(app);
1739
+ // Track server-owned sockets so forceCloseConnections can destroy upgraded
1740
+ // WS/SSE leftovers that closeAllConnections does not release.
1741
+ attachHttpServerConnectionTracker(server);
1427
1742
  let realtimeProxyRuntime = { stop: () => {} };
1428
1743
 
1429
1744
  // The relay service is constructed further below (it depends on the active
@@ -1444,11 +1759,16 @@ async function main(options = {}) {
1444
1759
  return {
1445
1760
  openCodePort,
1446
1761
  openCodeRunning: Boolean(openCodePort && isOpenCodeReady && !isRestartingOpenCode),
1762
+ openCodeStarting: Boolean(!isOpenCodeReady && !lastOpenCodeError),
1447
1763
  openCodeSecureConnection: isOpenCodeConnectionSecure(),
1448
1764
  openCodeAuthSource: openCodeAuthSource || null,
1449
1765
  openCodeApiPrefix: '',
1450
1766
  openCodeApiPrefixDetected: true,
1451
1767
  isOpenCodeReady,
1768
+ v1Migration,
1769
+ openCodeServeVersion,
1770
+ openCodeCliVersion,
1771
+ runtimeContract,
1452
1772
  lastOpenCodeError,
1453
1773
  lastOpenCodeLaunchDiagnostics,
1454
1774
  opencodeBinaryResolved: resolvedOpencodeBinary || null,
@@ -1559,6 +1879,8 @@ async function main(options = {}) {
1559
1879
  setAutoAcceptSession,
1560
1880
  sessionIndexService,
1561
1881
  sessionIndexSyncRuntime,
1882
+ // Production snapshot/lookup filter: committed Host archive authority.
1883
+ getCommittedHostMetadata: async () => sessionMetadataStore.getAll(),
1562
1884
  transcriptCacheService,
1563
1885
  });
1564
1886
  uiAuthController = bootstrapResult.uiAuthController;
@@ -1653,6 +1975,57 @@ async function main(options = {}) {
1653
1975
  isUnsafeSkillRelativePath,
1654
1976
  buildOpenCodeUrl,
1655
1977
  getOpenCodeAuthHeaders,
1978
+ getIsExternalOpenCode: () => isExternalOpenCode,
1979
+ getIsSharedOpenCodeService: () => openCodeLifecycleRuntime.isSharedOpenCodeService(),
1980
+ forceResolvedOpenCodeBinary,
1981
+ restartOpenCode,
1982
+ waitForOpenCodeReady,
1983
+ getRuntimeContract: () => runtimeContract,
1984
+ getOpenCodeServeVersion: () => openCodeServeVersion,
1985
+ getOpenCodeCliVersion: () => openCodeCliVersion,
1986
+ getResolvedOpenCodeBinary: () => resolvedOpencodeBinary,
1987
+ getResolvedOpenCodeBinarySource: () => resolvedOpencodeBinarySource,
1988
+ getActiveSessionCount,
1989
+ sessionMetadataStore,
1990
+ sessionArchiveService: {
1991
+ setArchive: (...args) => {
1992
+ if (!sessionArchiveServiceRef) {
1993
+ const error = new Error('session archive is not configured');
1994
+ error.status = 503;
1995
+ throw error;
1996
+ }
1997
+ return sessionArchiveServiceRef.setArchive(...args);
1998
+ },
1999
+ archiveSession: (...args) => {
2000
+ if (!sessionArchiveServiceRef) {
2001
+ const error = new Error('session archive is not configured');
2002
+ error.status = 503;
2003
+ throw error;
2004
+ }
2005
+ return sessionArchiveServiceRef.archiveSession(...args);
2006
+ },
2007
+ },
2008
+ // Same store seam as sessionGoalRuntime / scheduledTasksRuntime.
2009
+ readSessionMetadata: (sessionId) => sessionMetadataStore.get(sessionId),
2010
+ persistSessionMetadata: persistSessionMetadataToStore,
2011
+ onSystemSessionPersisted: hideSystemSessionFromIndex,
2012
+ persistSessionGoal: persistSessionGoalToStore,
2013
+ onSessionMetadataWritten: ({ sessionID, directory, metadata }) => {
2014
+ sessionTitleRuntime.processPayload({
2015
+ type: 'openchamber:session-metadata',
2016
+ properties: { sessionID, directory, metadata },
2017
+ }, directory || '');
2018
+ sessionGoalRuntime.processPayload({
2019
+ type: 'session.updated',
2020
+ properties: {
2021
+ info: {
2022
+ id: sessionID,
2023
+ directory: directory || '',
2024
+ metadata,
2025
+ },
2026
+ },
2027
+ }, directory || '');
2028
+ },
1656
2029
  getSmallModelService,
1657
2030
  getOpenCodePort: () => openCodePort,
1658
2031
  buildAugmentedPath,
@@ -1704,6 +2077,8 @@ async function main(options = {}) {
1704
2077
  getOpenCodeAuthHeaders,
1705
2078
  globalEventHub: globalMessageStreamHub,
1706
2079
  processForwardedEventPayload,
2080
+ projectOutboundSessionPayload: projectHostSessionPayload,
2081
+ ensureSessionMetadataReady,
1707
2082
  messageStreamWsClients: uiNotificationWsClients,
1708
2083
  upstreamStallTimeoutMs: getUpstreamStallTimeoutMs,
1709
2084
  terminalHeartbeatIntervalMs: TERMINAL_INPUT_WS_HEARTBEAT_INTERVAL_MS,
@@ -1773,12 +2148,9 @@ async function main(options = {}) {
1773
2148
  restartOpenCode: () => restartOpenCode(),
1774
2149
  getOpenCodeProcessInfo: () => {
1775
2150
  const managed = Boolean((openCodeProcess || openCodePort) && !ENV_SKIP_OPENCODE_START && !isExternalOpenCode);
1776
- // Only ever expose pid/port for a server WE manage. The Electron-side
1777
- // killer kills by port (lsof + kill -KILL), so returning a port we don't
1778
- // own — e.g. an external/desktop OpenCode on 4096 we attached to — would
1779
- // let a single miscomputed `managed` flag take down the user's separate
1780
- // server. Structurally withhold what isn't ours so the killer has no
1781
- // target, instead of relying on the flag check alone.
2151
+ // Only ever expose pid/port for a server WE manage. Desktop no longer runs a
2152
+ // post-stop detached killer of a pre-captured pid; still withhold non-owned
2153
+ // process identity so diagnostics cannot imply we may kill an external serve.
1782
2154
  return {
1783
2155
  managed,
1784
2156
  pid: managed && typeof openCodeProcess?.pid === 'number' ? openCodeProcess.pid : null,
@@ -1792,6 +2164,7 @@ async function main(options = {}) {
1792
2164
  unsubscribeScheduledTaskEvents();
1793
2165
  sessionIndexSyncRuntime?.stop();
1794
2166
  sessionIndexService?.close();
2167
+ sessionArchiveServiceRef?.stop?.();
1795
2168
  } catch {
1796
2169
  // The index is a local cache; a failed close must not block shutdown.
1797
2170
  }
@@ -1819,11 +2192,10 @@ async function main(options = {}) {
1819
2192
  // best-effort shutdown of the dictation worker
1820
2193
  }
1821
2194
  try {
1822
- if (smallModelServiceInstance?.stop) {
1823
- await smallModelServiceInstance.stop();
1824
- }
2195
+ const { stopLlmTempDirectory } = await import('./lib/llm/temp-directory.js');
2196
+ await stopLlmTempDirectory();
1825
2197
  } catch {
1826
- // best-effort cleanup of the small-model temp OpenCode directory
2198
+ // best-effort cleanup of the LLM generator temp OpenCode directory
1827
2199
  }
1828
2200
  return gracefulShutdown({
1829
2201
  exitProcess: shutdownOptions.exitProcess ?? false,