@vantran-se/openchamber-web 2.0.3 → 2.0.5

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 (265) hide show
  1. package/README.md +20 -28
  2. package/bin/cli.js +3 -1
  3. package/bin/cli.test.js +13 -4
  4. package/bin/lib/DOCUMENTATION.md +4 -1
  5. package/bin/lib/commands-serve.js +9 -0
  6. package/dist/assets/{CodeMirrorCommentWidgets-CWhdh0wf.js → CodeMirrorCommentWidgets-DCAQTLMM.js} +1 -1
  7. package/dist/assets/DiffView-uzNpHBlV.js +4 -0
  8. package/dist/assets/FilesView-Bkq0sjNx.js +17 -0
  9. package/dist/assets/{GitView-DKIjpnu4.js → GitView-BmibZC7n.js} +4 -4
  10. package/dist/assets/{GuestAttachDialog-BIa9jCfL.js → GuestAttachDialog-D7TJG1rt.js} +1 -1
  11. package/dist/assets/GuestSurfacePane-PAcYaFzP.js +1 -0
  12. package/dist/assets/{JsonTreeView-CtI1Mqwo.js → JsonTreeView-BNT2yUVu.js} +1 -1
  13. package/dist/assets/{LinearIssuesView-BD2zLu-r.js → LinearIssuesView-D3Azksju.js} +1 -1
  14. package/dist/assets/MarkdownImageGallery-BGY6tuiK.js +1 -0
  15. package/dist/assets/MarkdownRendererImpl-DJmhQ37f.js +12 -0
  16. package/dist/assets/{OnboardingScreen-B8TxloXZ.js → OnboardingScreen-D_41H2qC.js} +1 -1
  17. package/dist/assets/{PatchDiff--Civs1xY.js → PatchDiff-C0RSqa9W.js} +1 -1
  18. package/dist/assets/{PierreDiffViewer-BlDmw3-A.js → PierreDiffViewer-BBE7tTY2.js} +3 -3
  19. package/dist/assets/{PlanView-DyH2SyCP.js → PlanView-Bqc-xuDe.js} +4 -4
  20. package/dist/assets/PluginPane-DQ0k41Q-.js +3 -0
  21. package/dist/assets/SettingsView-BAiJU5hJ.js +30 -0
  22. package/dist/assets/{SettingsWindow-y9WpZqK6.js → SettingsWindow-DuC4ecLw.js} +1 -1
  23. package/dist/assets/{SubmoduleDiffSummary-DeFmhPFZ.js → SubmoduleDiffSummary-0HgmcMXz.js} +1 -1
  24. package/dist/assets/{ToolOutputDialog-8DmF7_PZ.js → ToolOutputDialog-DdHkQJEE.js} +6 -6
  25. package/dist/assets/{ToolPartDiffPreview-Ca15DKE1.js → ToolPartDiffPreview-DWdN0r5K.js} +2 -2
  26. package/dist/assets/{VirtualizedFileDiff-BfEBad09.js → VirtualizedFileDiff-ClDezf2d.js} +1 -1
  27. package/dist/assets/{WalkthroughView-Cj3K8Xz6.js → WalkthroughView-BJicp0RC.js} +2 -2
  28. package/dist/assets/{appThemeRegistry-DAdawJZn.js → appThemeRegistry-CiuyzfSW.js} +1 -1
  29. package/dist/assets/changesTree-CePHbFcj.js +1 -0
  30. package/dist/assets/commitAndPush-DTKb6IwT.js +1 -0
  31. package/dist/assets/{computeVirtualFileMetrics-RF-KjfUH.js → computeVirtualFileMetrics-kM_zJNMt.js} +2 -2
  32. package/dist/assets/de-BMnfXm-G.js +10 -0
  33. package/dist/assets/es-DKEvCCL6.js +16 -0
  34. package/dist/assets/fr-D9fgE2xE.js +14 -0
  35. package/dist/assets/index-DUke1nme.css +1 -0
  36. package/dist/assets/{index-CL7ZQ94-.js → index-DWB8bR_g.js} +1 -1
  37. package/dist/assets/index-Di6zUe5h.js +41 -0
  38. package/dist/assets/{isDiffPlainText-D2yJ9fO6.js → isDiffPlainText-DOoIsGqs.js} +1 -1
  39. package/dist/assets/{isFilePlainText-BwMEq1Ey.js → isFilePlainText-bt5VHvcd.js} +1 -1
  40. package/dist/assets/ja-BEXad-KW.js +16 -0
  41. package/dist/assets/ko-DGrQ0lEe.js +16 -0
  42. package/dist/assets/{main-quLSA7jc.js → main-BpLQbZC2.js} +2 -2
  43. package/dist/assets/main-CI9DtHJv.js +896 -0
  44. package/dist/assets/{markdown-shiki.worker-CahLvHIq.js → markdown-shiki.worker-Bg8SoAJJ.js} +8 -5
  45. package/dist/assets/markdownCore-DK2KK5RW.js +7 -0
  46. package/dist/assets/{miniChat-D-lJJibr.js → miniChat-CIWaR8wG.js} +2 -2
  47. package/dist/assets/{mobile-VTtphbW6.js → mobile-CnWKEYIP.js} +2 -2
  48. package/dist/assets/patchFileDiff-Cqn0C0az.js +13 -0
  49. package/dist/assets/pl-BlhkmR5b.js +16 -0
  50. package/dist/assets/pt-BR-Bs96Ew9I.js +16 -0
  51. package/dist/assets/{renderElectronMiniChatApp-DF7ch5eM.js → renderElectronMiniChatApp-D9t0-9nV.js} +2 -2
  52. package/dist/assets/renderMobileApp-CW7fVs_s.js +25 -0
  53. package/dist/assets/runtimeEndpointReset-D0PcCb3K.js +27 -0
  54. package/dist/assets/scheduledTasksApi-Dahv3ws0.js +1 -0
  55. package/dist/assets/{shikiHighlight-_HeCCHx1.js → shikiHighlight-l-MbaNS4.js} +1 -1
  56. package/dist/assets/{textMateThemeFromAppTheme-C95XdFGj.js → textMateThemeFromAppTheme-C-kGcOpD.js} +1 -1
  57. package/dist/assets/tr-CehUK1M9.js +7 -0
  58. package/dist/assets/uk-Bno5WGNz.js +16 -0
  59. package/dist/assets/{useAppFontEffects-ZM9ij19-.js → useAppFontEffects-DDjuwVqi.js} +160 -156
  60. package/dist/assets/{useInlineCommentController-CkfxMJPy.js → useInlineCommentController-Clg8EO3A.js} +2 -2
  61. package/dist/assets/{useRootScrollLock-BtiAC1fj.js → useRootScrollLock-nDcRrgJ1.js} +2 -2
  62. package/dist/assets/{vendor-codemirror-language-data-U-lXzXNK.js → vendor-codemirror-language-data-oKF0MAwd.js} +2 -2
  63. package/dist/assets/vendor-opencode-client-DSZl7jSi.js +12 -0
  64. package/dist/assets/vendor-zod-BSoePkpP.js +39 -0
  65. package/dist/assets/{web-components-C583Pmfm.js → web-components-Bc1WPd4i.js} +1 -1
  66. package/dist/assets/zh-CN-DnAweIWo.js +16 -0
  67. package/dist/assets/zh-TW-BbBBK25p.js +16 -0
  68. package/dist/index.html +5 -5
  69. package/dist/mini-chat.html +5 -5
  70. package/dist/mobile.html +5 -5
  71. package/package.json +3 -3
  72. package/server/index.d.ts +2 -0
  73. package/server/index.js +196 -52
  74. package/server/lib/agent-tool/DOCUMENTATION.md +5 -0
  75. package/server/lib/agent-tool/runtime.js +89 -51
  76. package/server/lib/agent-tool/runtime.test.js +17 -0
  77. package/server/lib/browser-control/DOCUMENTATION.md +14 -2
  78. package/server/lib/event-stream/DOCUMENTATION.md +2 -2
  79. package/server/lib/event-stream/global-hub.js +45 -7
  80. package/server/lib/event-stream/global-hub.test.js +128 -2
  81. package/server/lib/event-stream/global-ws-bridge.js +3 -1
  82. package/server/lib/event-stream/rebind.test.js +1 -0
  83. package/server/lib/event-stream/runtime.js +1 -2
  84. package/server/lib/event-stream/upstream-reader.js +3 -2
  85. package/server/lib/git/DOCUMENTATION.md +1 -1
  86. package/server/lib/git/service.js +22 -0
  87. package/server/lib/git/service.test.js +8 -0
  88. package/server/lib/guests/DOCUMENTATION.md +4 -3
  89. package/server/lib/guests/catalog.js +19 -1
  90. package/server/lib/guests/catalog.test.js +22 -0
  91. package/server/lib/guests/html-styles.js +5 -3
  92. package/server/lib/guests/html-styles.test.js +3 -1
  93. package/server/lib/guests/routes.js +11 -3
  94. package/server/lib/guests/service.js +5 -0
  95. package/server/lib/guests/surface.js +55 -9
  96. package/server/lib/guests/surface.test.js +56 -6
  97. package/server/lib/notifications/DOCUMENTATION.md +10 -0
  98. package/server/lib/notifications/emit-route.js +118 -0
  99. package/server/lib/notifications/emit-route.test.js +112 -0
  100. package/server/lib/openchamber-control/actions.js +16 -2
  101. package/server/lib/openchamber-control/service.js +23 -0
  102. package/server/lib/openchamber-control/service.test.js +31 -0
  103. package/server/lib/openchamber-sessions/fork-inheritance.js +103 -0
  104. package/server/lib/openchamber-sessions/fork-inheritance.test.js +69 -0
  105. package/server/lib/openchamber-sessions/routes.js +11 -0
  106. package/server/lib/openchamber-sessions/routes.test.js +18 -0
  107. package/server/lib/opencode/DOCUMENTATION.md +23 -23
  108. package/server/lib/opencode/bootstrap-runtime.js +14 -1
  109. package/server/lib/opencode/compatibility.js +6 -3
  110. package/server/lib/opencode/compatibility.test.js +8 -0
  111. package/server/lib/opencode/config-entity-v2.test.js +27 -0
  112. package/server/lib/opencode/config-paths.test.js +12 -0
  113. package/server/lib/opencode/config-v2.d.ts +2 -0
  114. package/server/lib/opencode/config-v2.js +42 -0
  115. package/server/lib/opencode/config-v2.test.js +17 -0
  116. package/server/lib/opencode/core-routes.js +20 -4
  117. package/server/lib/opencode/core-routes.test.js +41 -0
  118. package/server/lib/opencode/env-runtime.js +1 -8
  119. package/server/lib/opencode/feature-routes-runtime.js +2 -1
  120. package/server/lib/opencode/lifecycle.js +156 -2
  121. package/server/lib/opencode/lifecycle.test.js +255 -1
  122. package/server/lib/opencode/managed-config-file.js +18 -6
  123. package/server/lib/opencode/managed-config-file.test.js +23 -7
  124. package/server/lib/opencode/mcp.js +7 -2
  125. package/server/lib/opencode/network-runtime.js +16 -7
  126. package/server/lib/opencode/network-runtime.test.js +29 -1
  127. package/server/lib/opencode/npm-registry-config.d.ts +1 -0
  128. package/server/lib/opencode/npm-registry-config.js +123 -0
  129. package/server/lib/opencode/npm-registry-config.test.js +167 -0
  130. package/server/lib/opencode/npm-registry.js +5 -6
  131. package/server/lib/opencode/npm-registry.test.js +30 -0
  132. package/server/lib/opencode/openchamber-routes.js +29 -11
  133. package/server/lib/opencode/openchamber-routes.test.js +18 -0
  134. package/server/lib/opencode/project-directory-runtime.js +8 -0
  135. package/server/lib/opencode/project-directory-runtime.test.js +13 -0
  136. package/server/lib/opencode/provider-source-route.test.js +59 -0
  137. package/server/lib/opencode/providers.js +3 -0
  138. package/server/lib/opencode/providers.test.js +62 -0
  139. package/server/lib/opencode/proxy.js +53 -16
  140. package/server/lib/opencode/routes-upgrade.test.js +1 -1
  141. package/server/lib/opencode/routes.js +36 -1
  142. package/server/lib/opencode/server-startup-runtime.js +25 -0
  143. package/server/lib/opencode/server-startup-runtime.test.js +67 -1
  144. package/server/lib/opencode/server-utils-runtime.js +5 -0
  145. package/server/lib/opencode/settings-helpers.js +10 -0
  146. package/server/lib/opencode/settings-helpers.test.js +2 -2
  147. package/server/lib/opencode/settings-registry.json +17 -0
  148. package/server/lib/opencode/settings-runtime.js +1 -0
  149. package/server/lib/opencode/shared-service-runtime.js +155 -0
  150. package/server/lib/opencode/shared-service-runtime.test.js +180 -0
  151. package/server/lib/opencode/shared.js +49 -4
  152. package/server/lib/opencode/shared.test.js +78 -1
  153. package/server/lib/opencode/shutdown-runtime.js +14 -5
  154. package/server/lib/opencode/shutdown-runtime.test.js +77 -2
  155. package/server/lib/opencode/skill-routes.js +28 -12
  156. package/server/lib/opencode/skill-routes.test.js +121 -1
  157. package/server/lib/opencode/startup-pipeline-runtime.js +5 -4
  158. package/server/lib/opencode/startup-pipeline-runtime.test.js +18 -7
  159. package/server/lib/opencode/upgrade-capability.js +0 -14
  160. package/server/lib/opencode/v1-migration-topup.js +21 -15
  161. package/server/lib/opencode/v1-migration-topup.test.js +14 -6
  162. package/server/lib/opencode/v2-install.d.ts +1 -0
  163. package/server/lib/opencode/v2-install.js +8 -7
  164. package/server/lib/opencode/v2-install.test.js +13 -4
  165. package/server/lib/opencode/watcher.js +3 -1
  166. package/server/lib/opencode/watcher.test.js +54 -0
  167. package/server/lib/opencode/websearch-config.js +13 -1
  168. package/server/lib/quota/providers/kimi.js +1 -1
  169. package/server/lib/realtime-proxy.js +5 -1
  170. package/server/lib/realtime-proxy.test.js +29 -0
  171. package/server/lib/relay/tunnel-host.js +4 -1
  172. package/server/lib/relay/tunnel-host.test.js +11 -0
  173. package/server/lib/session-assist/DOCUMENTATION.md +17 -5
  174. package/server/lib/session-assist/runtime.js +19 -2
  175. package/server/lib/session-assist/runtime.test.js +8 -0
  176. package/server/lib/session-goal/DOCUMENTATION.md +11 -0
  177. package/server/lib/small-model/DOCUMENTATION.md +11 -10
  178. package/server/lib/small-model/index.js +27 -32
  179. package/server/lib/small-model/index.test.js +28 -16
  180. package/server/lib/spaces/DOCUMENTATION.md +404 -32
  181. package/server/lib/spaces/code-in-bait.js +145 -6
  182. package/server/lib/spaces/code-in.js +10 -150
  183. package/server/lib/spaces/code-in.test.js +7 -83
  184. package/server/lib/spaces/code-out.js +1819 -0
  185. package/server/lib/spaces/code-out.test.js +3945 -0
  186. package/server/lib/spaces/code-transfer.js +173 -0
  187. package/server/lib/spaces/command-stream.test.js +181 -0
  188. package/server/lib/spaces/dispatcher.js +670 -0
  189. package/server/lib/spaces/dispatcher.test.js +582 -0
  190. package/server/lib/spaces/gatekeeper-channel.js +61 -7
  191. package/server/lib/spaces/gatekeeper-channel.test.js +43 -7
  192. package/server/lib/spaces/gatekeeper-program.cjs +68 -14
  193. package/server/lib/spaces/gatekeeper-program.test.js +118 -3
  194. package/server/lib/spaces/hardening.js +20 -4
  195. package/server/lib/spaces/hardening.test.js +18 -3
  196. package/server/lib/spaces/host-git.js +7 -4
  197. package/server/lib/spaces/host-git.test.js +6 -2
  198. package/server/lib/spaces/host.js +310 -0
  199. package/server/lib/spaces/host.test.js +273 -0
  200. package/server/lib/spaces/journey.js +552 -0
  201. package/server/lib/spaces/journey.test.js +589 -0
  202. package/server/lib/spaces/layout.js +42 -8
  203. package/server/lib/spaces/manager.js +4 -3
  204. package/server/lib/spaces/manager.test.js +6 -0
  205. package/server/lib/spaces/places/code-out.docker.live.test.js +412 -0
  206. package/server/lib/spaces/places/contract-suite.js +36 -0
  207. package/server/lib/spaces/places/dispatcher.docker.live.test.js +296 -0
  208. package/server/lib/spaces/places/docker-live-support.js +27 -4
  209. package/server/lib/spaces/places/docker.js +33 -5
  210. package/server/lib/spaces/places/docker.test.js +42 -3
  211. package/server/lib/spaces/places/escape-suite.js +118 -14
  212. package/server/lib/spaces/places/fake-docker.js +3 -1
  213. package/server/lib/spaces/places/grants.docker.live.test.js +197 -0
  214. package/server/lib/spaces/places/memory-place.js +24 -1
  215. package/server/lib/spaces/places/registry.js +1 -1
  216. package/server/lib/spaces/places/server.docker.live.test.js +69 -33
  217. package/server/lib/spaces/places/sessions.docker.live.test.js +222 -0
  218. package/server/lib/spaces/routes.js +216 -0
  219. package/server/lib/spaces/routes.test.js +213 -0
  220. package/server/lib/spaces/run-command.js +261 -8
  221. package/server/lib/spaces/run-command.test.js +193 -3
  222. package/server/lib/spaces/space-events.js +153 -0
  223. package/server/lib/spaces/space-events.test.js +160 -0
  224. package/server/lib/spaces/space-opencode.js +52 -0
  225. package/server/lib/spaces/space-opencode.test.js +41 -0
  226. package/server/lib/spaces/space-records.js +150 -0
  227. package/server/lib/spaces/space-records.test.js +113 -0
  228. package/server/lib/spaces/space-sessions.js +210 -0
  229. package/server/lib/spaces/space-sessions.test.js +161 -0
  230. package/server/lib/spaces/websocket.js +329 -0
  231. package/server/lib/spaces/websocket.test.js +345 -0
  232. package/server/lib/ui-auth/ui-auth.js +8 -2
  233. package/server/lib/ui-auth/ui-auth.test.js +29 -0
  234. package/server/opencode-proxy.test.js +271 -0
  235. package/server/proxy-headers.js +8 -2
  236. package/server/proxy-headers.test.js +20 -0
  237. package/dist/assets/DiffView-CX3wAX2S.js +0 -4
  238. package/dist/assets/FilesView-OL3CON3r.js +0 -17
  239. package/dist/assets/GuestSurfacePane-B_cqX5I5.js +0 -1
  240. package/dist/assets/MarkdownImageGallery-DFiTFZgj.js +0 -1
  241. package/dist/assets/MarkdownRendererImpl-DZm5sf1W.js +0 -12
  242. package/dist/assets/PluginPane-BibJit86.js +0 -3
  243. package/dist/assets/SettingsView-h-VpVO47.js +0 -29
  244. package/dist/assets/commitAndPush-BIPfOMXY.js +0 -1
  245. package/dist/assets/de-BqCNGROu.js +0 -10
  246. package/dist/assets/es-BTPpTRgN.js +0 -16
  247. package/dist/assets/fr-hxbzMdAg.js +0 -14
  248. package/dist/assets/index-CLcM8HKx.css +0 -1
  249. package/dist/assets/index-CvLgnuNI.js +0 -41
  250. package/dist/assets/ja-_zLwwYiy.js +0 -16
  251. package/dist/assets/ko-CFn3Dzhc.js +0 -16
  252. package/dist/assets/main-BLJaug9I.js +0 -896
  253. package/dist/assets/markdownCore-DS9FdwrI.js +0 -5
  254. package/dist/assets/patchFileDiff-D9iBcTEi.js +0 -13
  255. package/dist/assets/pl-DFHE7xac.js +0 -16
  256. package/dist/assets/pt-BR-CUjZF179.js +0 -16
  257. package/dist/assets/renderMobileApp-CNQhdXsi.js +0 -25
  258. package/dist/assets/runtimeEndpointReset-DeaERc0M.js +0 -27
  259. package/dist/assets/scheduledTasksApi-BD6Vx9pu.js +0 -1
  260. package/dist/assets/tr-BB6sA_zH.js +0 -7
  261. package/dist/assets/uk-CB4o8g-o.js +0 -16
  262. package/dist/assets/vendor-opencode-client-_BAHQo4R.js +0 -12
  263. package/dist/assets/vendor-zod-Gea9uBlY.js +0 -39
  264. package/dist/assets/zh-CN-BRyDuWhc.js +0 -16
  265. package/dist/assets/zh-TW-BDGbWA1Q.js +0 -16
package/dist/index.html CHANGED
@@ -515,17 +515,17 @@
515
515
  pointer-events: none;
516
516
  }
517
517
  </style>
518
- <script type="module" crossorigin src="/assets/main-quLSA7jc.js"></script>
518
+ <script type="module" crossorigin src="/assets/main-BpLQbZC2.js"></script>
519
519
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
520
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-Gea9uBlY.js">
520
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
521
521
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
522
522
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
523
523
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
524
- <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-_BAHQo4R.js">
525
- <link rel="modulepreload" crossorigin href="/assets/index-CvLgnuNI.js">
524
+ <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
525
+ <link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
526
526
  <link rel="modulepreload" crossorigin href="/assets/contextPanelEmbeddedChat-CirXEPJq.js">
527
527
  <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
528
- <link rel="stylesheet" crossorigin href="/assets/index-CLcM8HKx.css">
528
+ <link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
529
529
  <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
530
530
  </head>
531
531
  <body class="h-full bg-background text-foreground">
@@ -74,16 +74,16 @@
74
74
  }
75
75
  </style>
76
76
 
77
- <script type="module" crossorigin src="/assets/miniChat-D-lJJibr.js"></script>
77
+ <script type="module" crossorigin src="/assets/miniChat-CIWaR8wG.js"></script>
78
78
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
79
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-Gea9uBlY.js">
79
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
80
80
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
81
81
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
83
- <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-_BAHQo4R.js">
84
- <link rel="modulepreload" crossorigin href="/assets/index-CvLgnuNI.js">
83
+ <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
84
+ <link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
85
85
  <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
86
- <link rel="stylesheet" crossorigin href="/assets/index-CLcM8HKx.css">
86
+ <link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
87
87
  <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
88
88
  </head>
89
89
  <body class="h-full bg-background text-foreground">
package/dist/mobile.html CHANGED
@@ -63,16 +63,16 @@
63
63
  })();
64
64
  </script>
65
65
 
66
- <script type="module" crossorigin src="/assets/mobile-VTtphbW6.js"></script>
66
+ <script type="module" crossorigin src="/assets/mobile-CnWKEYIP.js"></script>
67
67
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
68
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-Gea9uBlY.js">
68
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
69
69
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
70
70
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
71
71
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
72
- <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-_BAHQo4R.js">
73
- <link rel="modulepreload" crossorigin href="/assets/index-CvLgnuNI.js">
72
+ <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
73
+ <link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
74
74
  <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
75
- <link rel="stylesheet" crossorigin href="/assets/index-CLcM8HKx.css">
75
+ <link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
76
76
  <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
77
77
  </head>
78
78
  <body class="h-full bg-background text-foreground">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantran-se/openchamber-web",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
@@ -27,8 +27,8 @@
27
27
  "@clack/prompts": "^1.1.0",
28
28
  "@octokit/rest": "^22.0.1",
29
29
  "@openchamber/sdk": "2.0.0",
30
- "@opencode/client": "2.0.15",
31
- "@opencode/schema": "2.0.15",
30
+ "@opencode/client": "2.0.18",
31
+ "@opencode/schema": "2.0.18",
32
32
  "@simplewebauthn/server": "13.3.3",
33
33
  "bun-pty": "^0.4.5",
34
34
  "compression": "^1.8.1",
package/server/index.d.ts CHANGED
@@ -32,6 +32,8 @@ export interface StartWebUiServerOptions {
32
32
  attachSignals?: boolean;
33
33
  exitOnShutdown?: boolean;
34
34
  uiPassword?: string | null;
35
+ /** Selects the owned Desktop lifecycle or the shared local Web service. Explicit OPENCODE_HOST still wins. */
36
+ openCodeLifecycleMode?: "managed" | "shared";
35
37
  desktopUpdater?: DesktopUpdater;
36
38
  /** App-owned built-in resources outside Electron's ASAR archive. */
37
39
  builtInExtensionsDir?: string;
package/server/index.js CHANGED
@@ -51,7 +51,13 @@ import {
51
51
  UPSTREAM_STALL_TIMEOUT_CONCURRENT_MS,
52
52
  } from './lib/event-stream/index.js';
53
53
  import { createFsSearchRuntime as createFsSearchRuntimeFactory } from './lib/fs/search.js';
54
- import { createOpenCodeLifecycleRuntime } from './lib/opencode/lifecycle.js';
54
+ import {
55
+ createOpenCodeLifecycleControls,
56
+ createOpenCodeLifecycleRuntime,
57
+ createOpenCodeRecoveryCallbacks,
58
+ createOpenCodeServerComposition,
59
+ } from './lib/opencode/lifecycle.js';
60
+ import { createSharedOpenCodeServiceRuntime } from './lib/opencode/shared-service-runtime.js';
55
61
  import { createOpenCodeEnvRuntime } from './lib/opencode/env-runtime.js';
56
62
  import { providedLoginShellEnvSnapshot } from './lib/opencode/login-shell-env.js';
57
63
  import { resolveOpenCodeEnvConfig } from './lib/opencode/env-config.js';
@@ -112,6 +118,8 @@ import { createAgentMemoryRuntime } from './lib/agent-memory/runtime.js';
112
118
  import { createAgentMemoryActions } from './lib/agent-memory/actions.js';
113
119
  import { createMemoryProjectResolver } from './lib/agent-memory/project-resolution.js';
114
120
  import { isAgentMemoryFeatureAvailable } from './lib/agent-memory/feature-flag.js';
121
+ import { createSpacesHost } from './lib/spaces/host.js';
122
+ import { createSwitchController, registerSpaceRoutes } from './lib/spaces/routes.js';
115
123
  import { resolvePrimaryWorktreeRoot } from './lib/git/service.js';
116
124
  import { createRemoteClientAuthRuntime } from './lib/client-auth/remote-clients.js';
117
125
  import { createClientPairingRuntime } from './lib/client-auth/pairing.js';
@@ -129,6 +137,7 @@ import { createOpenChamberSessionService } from './lib/openchamber-sessions/rout
129
137
  import { createSessionMetadataStore, createOpenCodeSessionMetadata } from './lib/openchamber-sessions/session-metadata-store.js';
130
138
  import { createScheduledTaskService } from './lib/scheduled-tasks/service.js';
131
139
  import { createOpenChamberControlService } from './lib/openchamber-control/service.js';
140
+ import { createPluginNotificationEmitter } from './lib/notifications/emit-route.js';
132
141
  import { OpenChamberControlError } from './lib/openchamber-control/error.js';
133
142
  import { createFileOpenRequester } from './lib/openchamber-control/file-open.js';
134
143
  import { applyConnectAttemptTimeout } from './lib/network-defaults.js';
@@ -386,6 +395,8 @@ const projectDirectoryRuntime = createProjectDirectoryRuntime({
386
395
  normalizeDirectoryPath,
387
396
  getReadSettingsFromDiskMigrated: () => readSettingsFromDiskMigrated,
388
397
  sanitizeProjects,
398
+ // A space's directory never runs on the host, whatever route carries it.
399
+ refuseDirectory: (candidate) => spacesHost?.refuseDirectory(candidate) ?? null,
389
400
  });
390
401
 
391
402
  const resolveDirectoryCandidate = (...args) => projectDirectoryRuntime.resolveDirectoryCandidate(...args);
@@ -629,6 +640,9 @@ let openCodeNotReadySince = 0;
629
640
  let isExternalOpenCode = false;
630
641
  let exitOnShutdown = true;
631
642
  let uiAuthController = null;
643
+ // The isolated-spaces host: the place, the manager and the dispatcher. Null while the feature's
644
+ // switch is off, and then nothing of the feature runs, see docs/isolated-spaces/DESIGN.md.
645
+ let spacesHost = null;
632
646
  let activeTunnelController = null;
633
647
  let globalWatcherStartPromise = null;
634
648
  const tunnelProviderRegistry = createTunnelProviderRegistry([
@@ -655,6 +669,8 @@ const initialOpenCodeAuthState = hmrStateRuntime.resolveOpenCodeAuthFromState({
655
669
  });
656
670
  let openCodeAuthPassword = initialOpenCodeAuthState.openCodeAuthPassword;
657
671
  let openCodeAuthSource = initialOpenCodeAuthState.openCodeAuthSource;
672
+ let openCodeConnectionAdapter = null;
673
+ let sharedOpenCodeServiceRuntime = null;
658
674
 
659
675
  // Sync helper - call after modifying any HMR state variable
660
676
  const syncToHmrState = () => {
@@ -735,7 +751,23 @@ const openCodeAuthStateRuntime = createOpenCodeAuthStateRuntime({
735
751
  syncToHmrState,
736
752
  });
737
753
 
738
- const getOpenCodeAuthHeaders = (...args) => openCodeAuthStateRuntime.getOpenCodeAuthHeaders(...args);
754
+ const getManagedOpenCodeAuthHeaders = (...args) => openCodeAuthStateRuntime.getOpenCodeAuthHeaders(...args);
755
+ const getConnectionOpenCodeAuthHeaders = (...args) => (
756
+ openCodeConnectionAdapter?.getOpenCodeAuthHeaders(...args) ?? getManagedOpenCodeAuthHeaders(...args)
757
+ );
758
+ const getOpenCodeBaseUrl = () => openCodeConnectionAdapter?.getOpenCodeBaseUrl() ?? openCodeBaseUrl;
759
+ const getOpenCodeConnectionKind = () => (
760
+ openCodeConnectionAdapter?.getOpenCodeConnectionKind() ?? 'managed-owned'
761
+ );
762
+ const ownsOpenCodeProcess = () => openCodeConnectionAdapter?.ownsOpenCodeProcess() ?? true;
763
+ const setSharedOpenCodeConnection = (connection) => {
764
+ openCodeBaseUrl = connection.endpoint.url;
765
+ const parsedUrl = new URL(connection.endpoint.url);
766
+ const parsedPort = parsedUrl.port
767
+ ? Number.parseInt(parsedUrl.port, 10)
768
+ : (parsedUrl.protocol === 'https:' ? 443 : 80);
769
+ openCodePort = Number.isFinite(parsedPort) ? parsedPort : null;
770
+ };
739
771
  const isOpenCodeConnectionSecure = (...args) => openCodeAuthStateRuntime.isOpenCodeConnectionSecure(...args);
740
772
  const ensureLocalOpenCodeServerPassword = (...args) => openCodeAuthStateRuntime.ensureLocalOpenCodeServerPassword(...args);
741
773
 
@@ -750,10 +782,12 @@ Object.defineProperties(openCodeNetworkState, {
750
782
 
751
783
  const openCodeNetworkRuntime = createOpenCodeNetworkRuntime({
752
784
  state: openCodeNetworkState,
753
- getOpenCodeAuthHeaders,
785
+ getOpenCodeBaseUrl,
786
+ getOpenCodeAuthHeaders: getConnectionOpenCodeAuthHeaders,
754
787
  configuredOpenCodeHostname: ENV_CONFIGURED_OPENCODE_HOSTNAME,
755
788
  });
756
789
 
790
+ const getOpenCodeAuthHeaders = (...args) => openCodeNetworkRuntime.getOpenCodeAuthHeaders(...args);
757
791
  const waitForReady = (...args) => openCodeNetworkRuntime.waitForReady(...args);
758
792
  const normalizeApiPrefix = (...args) => openCodeNetworkRuntime.normalizeApiPrefix(...args);
759
793
  const setDetectedOpenCodeApiPrefix = (...args) => openCodeNetworkRuntime.setDetectedOpenCodeApiPrefix(...args);
@@ -1105,6 +1139,10 @@ const serverUtilsRuntime = createServerUtilsRuntime({
1105
1139
  // down, while the proxy is registered later still.
1106
1140
  getArchivedSessions: () => openChamberSessionService.archiveStore.getAll(),
1107
1141
  getStoredSessionMetadata: () => sessionMetadataStore.listUnmigrated(),
1142
+ // Isolated spaces: with the switch on, the session list carries every space's sessions and
1143
+ // the global SSE stream their events. Called, not captured: the host is made in `main`.
1144
+ getMergeSpaceSessionList: () => (spacesHost ? (payload) => spacesHost.mergeSessionList(payload) : null),
1145
+ getSpaceEventHub: () => (spacesHost ? globalMessageStreamHub : null),
1108
1146
  fs,
1109
1147
  os,
1110
1148
  path,
@@ -1113,7 +1151,7 @@ const serverUtilsRuntime = createServerUtilsRuntime({
1113
1151
  longRequestTimeoutMs: LONG_REQUEST_TIMEOUT_MS,
1114
1152
  getRuntime: () => ({
1115
1153
  openCodePort,
1116
- openCodeBaseUrl,
1154
+ openCodeBaseUrl: getOpenCodeBaseUrl(),
1117
1155
  openCodeNotReadySince,
1118
1156
  isOpenCodeReady,
1119
1157
  isRestartingOpenCode,
@@ -1260,6 +1298,19 @@ Object.defineProperties(openCodeLifecycleState, {
1260
1298
  resolvedWslDistro: { get: () => resolvedWslDistro, set: (value) => { resolvedWslDistro = value; } },
1261
1299
  });
1262
1300
 
1301
+ const openCodeRecoveryCallbacks = createOpenCodeRecoveryCallbacks({
1302
+ resetOpenCodeRuntimeProviders,
1303
+ rebindUpstream: () => {
1304
+ if (messageStreamRuntime) {
1305
+ messageStreamRuntime.rebindUpstream();
1306
+ } else {
1307
+ globalMessageStreamHub.rebind();
1308
+ }
1309
+ },
1310
+ interruptBusySessionsAfterRestart: () => sessionRuntime.interruptBusySessionsAfterRestart(),
1311
+ broadcastUiNotification,
1312
+ });
1313
+
1263
1314
  const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
1264
1315
  state: openCodeLifecycleState,
1265
1316
  env: {
@@ -1314,41 +1365,16 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
1314
1365
  // so the UI keeps receiving events instead of staying pinned to the old
1315
1366
  // process (#2638). The runtime is created later by the startup pipeline;
1316
1367
  // by the time any restart runs, it is assigned.
1317
- onOpenCodeRestarted: () => {
1318
- // A restart reloads plugins: provider ports, credentials and the provider
1319
- // list itself can all differ from what was cached.
1320
- resetOpenCodeRuntimeProviders();
1321
- try {
1322
- messageStreamRuntime?.rebindUpstream();
1323
- } catch (error) {
1324
- console.warn('Failed to rebind message stream after OpenCode restart:', error?.message ?? error);
1325
- }
1326
- try {
1327
- const { sessionIds } = sessionRuntime.interruptBusySessionsAfterRestart();
1328
- if (sessionIds.length > 0) {
1329
- const multiple = sessionIds.length > 1;
1330
- broadcastUiNotification({
1331
- title: multiple ? 'Chats interrupted' : 'Chat interrupted',
1332
- body: multiple
1333
- ? 'OpenCode restarted during running responses. Send a message in each chat to continue.'
1334
- : 'OpenCode restarted during a running response. Send a message to continue.',
1335
- tag: 'opencode-restart-interrupted',
1336
- kind: 'opencode-restart-interrupted',
1337
- sessionId: sessionIds[0],
1338
- });
1339
- }
1340
- } catch (error) {
1341
- console.warn('Failed to reconcile sessions after OpenCode restart:', error?.message ?? error);
1342
- }
1343
- },
1368
+ onOpenCodeRestarted: () => openCodeRecoveryCallbacks.onManagedRestarted(),
1344
1369
  getManagedOpenCodeEnv: async () => (managedConfigRuntime ? managedConfigRuntime.buildManagedChildEnv() : {}),
1345
1370
  });
1346
1371
 
1347
1372
  const getOpenCodeCompatibility = async () => {
1348
- if (isExternalOpenCode || ENV_SKIP_OPENCODE_START) {
1349
- const base = ENV_CONFIGURED_OPENCODE_HOST?.origin || openCodeBaseUrl || `http://127.0.0.1:${openCodePort || ENV_EFFECTIVE_PORT}`;
1373
+ if (getOpenCodeConnectionKind() !== 'managed-owned' || ENV_SKIP_OPENCODE_START) {
1374
+ const base = ENV_CONFIGURED_OPENCODE_HOST?.origin || getOpenCodeBaseUrl() || `http://127.0.0.1:${openCodePort || ENV_EFFECTIVE_PORT}`;
1350
1375
  const version = await readExternalOpenCodeVersion(base, getOpenCodeAuthHeaders()).catch(() => null);
1351
- return describeOpenCodeCompatibility(version, 'external', false);
1376
+ const installation = getOpenCodeConnectionKind() === 'shared-local' ? 'shared' : 'external';
1377
+ return describeOpenCodeCompatibility(version, installation, false);
1352
1378
  }
1353
1379
  const binary = ensureOpencodeCliEnv();
1354
1380
  const installation = isBundledOpenCodeCliPath(binary) ? 'bundled' : 'managed';
@@ -1357,6 +1383,14 @@ const getOpenCodeCompatibility = async () => {
1357
1383
  };
1358
1384
 
1359
1385
  const getOpenCodeUpgradeCapability = () => {
1386
+ if (getOpenCodeConnectionKind() === 'shared-local') {
1387
+ return resolveOpenCodeUpgradeCapability({
1388
+ isExternal: true,
1389
+ hasManagedProcess: false,
1390
+ activeBinary: null,
1391
+ isBundledBinary: isBundledOpenCodeCliPath,
1392
+ });
1393
+ }
1360
1394
  const activeBinary = lastOpenCodeLaunchDiagnostics?.sourceBinary
1361
1395
  || lastOpenCodeLaunchDiagnostics?.binary
1362
1396
  || resolvedOpencodeBinary;
@@ -1368,12 +1402,19 @@ const getOpenCodeUpgradeCapability = () => {
1368
1402
  });
1369
1403
  };
1370
1404
 
1371
- const restartOpenCode = (...args) => openCodeLifecycleRuntime.restartOpenCode(...args);
1405
+ const openCodeLifecycleControls = createOpenCodeLifecycleControls({
1406
+ getKind: getOpenCodeConnectionKind,
1407
+ restartLifecycle: (...args) => openCodeLifecycleRuntime.restartOpenCode(...args),
1408
+ triggerLifecycleHealthCheck: (...args) => openCodeLifecycleRuntime.triggerHealthCheck(...args),
1409
+ recoverShared: () => sharedOpenCodeServiceRuntime.recover(),
1410
+ onSharedRecovered: openCodeRecoveryCallbacks.onSharedRecovered,
1411
+ });
1412
+ const restartOpenCode = (...args) => openCodeLifecycleControls.restart(...args);
1372
1413
  const waitForOpenCodeReady = (...args) => openCodeLifecycleRuntime.waitForOpenCodeReady(...args);
1373
1414
  const waitForAgentPresence = (...args) => openCodeLifecycleRuntime.waitForAgentPresence(...args);
1374
1415
  const refreshOpenCodeAfterConfigChange = (...args) => openCodeLifecycleRuntime.refreshOpenCodeAfterConfigChange(...args);
1375
1416
  const startHealthMonitoring = () => openCodeLifecycleRuntime.startHealthMonitoring(HEALTH_CHECK_INTERVAL);
1376
- const triggerHealthCheck = () => openCodeLifecycleRuntime.triggerHealthCheck();
1417
+ const triggerHealthCheck = (...args) => openCodeLifecycleControls.triggerHealthCheck(...args);
1377
1418
  const scheduledTasksRuntime = createScheduledTasksRuntime({
1378
1419
  projectConfigRuntime,
1379
1420
  listProjects: async () => {
@@ -1563,6 +1604,12 @@ const fileOpenRequester = createFileOpenRequester({
1563
1604
  },
1564
1605
  });
1565
1606
 
1607
+ const pluginNotificationEmitter = createPluginNotificationEmitter({
1608
+ readSettingsFromDiskMigrated,
1609
+ emitDesktopNotification,
1610
+ broadcastUiNotification,
1611
+ });
1612
+
1566
1613
  const openChamberControlService = createOpenChamberControlService({
1567
1614
  readSettingsFromDiskMigrated,
1568
1615
  sanitizeProjects,
@@ -1573,6 +1620,15 @@ const openChamberControlService = createOpenChamberControlService({
1573
1620
  scheduledTaskService,
1574
1621
  browserControl: browserControlRouter,
1575
1622
  fileOpen: fileOpenRequester,
1623
+ // The tool is off by default; a plugin generated before it was switched off
1624
+ // must not keep paging the user.
1625
+ notifyUser: async (input) => {
1626
+ const settings = await readSettingsFromDiskMigrated().catch(() => null);
1627
+ if (settings?.agentNotifyToolEnabled !== true) {
1628
+ return { status: 403, body: { error: 'The notify tool is turned off in OpenChamber settings' } };
1629
+ }
1630
+ return pluginNotificationEmitter.emit(input);
1631
+ },
1576
1632
  agentMemoryActions: createAgentMemoryActions({
1577
1633
  agentMemoryRuntime,
1578
1634
  createError: (message, status) => new OpenChamberControlError(message, status),
@@ -1595,9 +1651,19 @@ const ensureGlobalWatcherStarted = async () => {
1595
1651
  return globalWatcherStartPromise;
1596
1652
  };
1597
1653
  const bootstrapOpenCodeAtStartup = async (...args) => {
1598
- await openCodeLifecycleRuntime.bootstrapOpenCodeAtStartup(...args);
1654
+ if (getOpenCodeConnectionKind() === 'shared-local') {
1655
+ const connection = await sharedOpenCodeServiceRuntime.connect();
1656
+ setSharedOpenCodeConnection(connection);
1657
+ isOpenCodeReady = true;
1658
+ isExternalOpenCode = true;
1659
+ lastOpenCodeError = null;
1660
+ openCodeNotReadySince = 0;
1661
+ syncToHmrState();
1662
+ } else {
1663
+ await openCodeLifecycleRuntime.bootstrapOpenCodeAtStartup(...args);
1664
+ }
1599
1665
  scheduleOpenCodeApiDetection();
1600
- if (openCodeLifecycleState.openCodeProcess && !openCodeLifecycleState.isExternalOpenCode) {
1666
+ if (ownsOpenCodeProcess() && openCodeLifecycleState.openCodeProcess && !openCodeLifecycleState.isExternalOpenCode) {
1601
1667
  startHealthMonitoring();
1602
1668
  }
1603
1669
  // The global watcher used to start only for desktop notifications; the
@@ -1634,6 +1700,7 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
1634
1700
  contextObligatoryRuntime,
1635
1701
  messageQueueRuntime,
1636
1702
  sessionRuntime,
1703
+ globalEventHub: globalMessageStreamHub,
1637
1704
  getHealthCheckInterval: () => healthCheckInterval,
1638
1705
  clearHealthCheckInterval: (value) => clearInterval(value),
1639
1706
  getTerminalRuntime: () => terminalRuntime,
@@ -1644,7 +1711,7 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
1644
1711
  setMessageStreamRuntime: (value) => {
1645
1712
  messageStreamRuntime = value;
1646
1713
  },
1647
- shouldSkipOpenCodeStop: () => ENV_SKIP_OPENCODE_START || isExternalOpenCode,
1714
+ getOpenCodeConnectionKind,
1648
1715
  getOpenCodePort: () => openCodePort,
1649
1716
  getOpenCodeProcess: () => openCodeProcess,
1650
1717
  setOpenCodeProcess: (value) => {
@@ -1652,6 +1719,7 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
1652
1719
  },
1653
1720
  killProcessOnPort,
1654
1721
  waitForPortRelease,
1722
+ disposeSharedOpenCodeService: () => sharedOpenCodeServiceRuntime?.dispose(),
1655
1723
  getServer: () => server,
1656
1724
  getUiAuthController: () => uiAuthController,
1657
1725
  setUiAuthController: (value) => {
@@ -1670,12 +1738,18 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
1670
1738
  getDictationRuntime: () => dictationRuntime,
1671
1739
  getRelayService: () => relayServiceInstance,
1672
1740
  getRelayReconcileTimer: () => relayReconcileTimer,
1741
+ getSpacesHost: () => spacesHost,
1673
1742
  });
1674
1743
 
1675
1744
  const gracefulShutdown = (...args) => gracefulShutdownRuntime.gracefulShutdown(...args);
1676
1745
 
1677
1746
  async function main(options = {}) {
1678
1747
  beginGuestServiceHost();
1748
+ const runtimeName = process.env.OPENCHAMBER_RUNTIME || 'web';
1749
+ const lifecycleMode = options.openCodeLifecycleMode === 'managed'
1750
+ || options.openCodeLifecycleMode === 'shared'
1751
+ ? options.openCodeLifecycleMode
1752
+ : undefined;
1679
1753
  const port = Number.isFinite(options.port) && options.port >= 0 ? Math.trunc(options.port) : DEFAULT_PORT;
1680
1754
  const host = typeof options.host === 'string' && options.host.length > 0 ? options.host : undefined;
1681
1755
  const effectiveBindHost = host
@@ -1687,7 +1761,6 @@ async function main(options = {}) {
1687
1761
  fsPromises,
1688
1762
  path,
1689
1763
  dataDir: OPENCHAMBER_DATA_DIR,
1690
- env: process.env,
1691
1764
  executeAction: (...args) => openChamberControlService.execute(...args),
1692
1765
  // A v2 tool call carries no directory, only the session it runs in.
1693
1766
  resolveSessionDirectory: (sessionID) => openChamberControlService.resolveSessionDirectory(sessionID),
@@ -1698,15 +1771,31 @@ async function main(options = {}) {
1698
1771
  // A pipe listener reports a string here, which has no address to bind back to.
1699
1772
  getActiveHost: () => server?.address?.()?.address ?? null,
1700
1773
  });
1701
- managedConfigRuntime = createManagedConfigRuntime({
1702
- fsPromises,
1703
- path,
1704
- dataDir: OPENCHAMBER_DATA_DIR,
1705
- env: process.env,
1706
- agentToolRuntime,
1707
- readSettings: () => readSettingsFromDiskMigrated(),
1708
- isAgentMemoryAvailable: isAgentMemoryFeatureAvailable,
1774
+ const openCodeComposition = createOpenCodeServerComposition({
1775
+ runtime: runtimeName,
1776
+ configuredHost: ENV_CONFIGURED_OPENCODE_HOST,
1777
+ lifecycleMode,
1778
+ createSharedRuntime: () => {
1779
+ const binary = ensureOpencodeCliEnv();
1780
+ return createSharedOpenCodeServiceRuntime({
1781
+ ensureOptions: binary ? { command: [binary, 'serve', '--service'] } : undefined,
1782
+ });
1783
+ },
1784
+ createManagedPluginRuntime: () => createManagedConfigRuntime({
1785
+ fsPromises,
1786
+ path,
1787
+ dataDir: OPENCHAMBER_DATA_DIR,
1788
+ env: process.env,
1789
+ agentToolRuntime,
1790
+ readSettings: () => readSettingsFromDiskMigrated(),
1791
+ isAgentMemoryAvailable: isAgentMemoryFeatureAvailable,
1792
+ }),
1793
+ getManagedBaseUrl: () => openCodeBaseUrl,
1794
+ getManagedAuthHeaders: getManagedOpenCodeAuthHeaders,
1709
1795
  });
1796
+ sharedOpenCodeServiceRuntime = openCodeComposition.sharedRuntime;
1797
+ managedConfigRuntime = openCodeComposition.managedPluginRuntime;
1798
+ openCodeConnectionAdapter = openCodeComposition.connection;
1710
1799
 
1711
1800
  // Pairing transports advertised to the create-device dialog. LAN reachability is
1712
1801
  // derived from the SERVER's actual bind (a wildcard bind → the machine's LAN IP;
@@ -1846,6 +1935,31 @@ async function main(options = {}) {
1846
1935
  // but do not hold server listen or managed OpenCode startup on `say -v "?"`.
1847
1936
  const sayTTSCapability = detectSayTtsCapability(process);
1848
1937
 
1938
+ // The isolated-spaces switch, read here at start and changed live through its route below.
1939
+ // While it is off the feature has no place, no manager, no route and runs no `docker`.
1940
+ const buildSpacesHost = () => createSpacesHost({
1941
+ dataDir: OPENCHAMBER_DATA_DIR,
1942
+ dockerPath: searchPathFor('docker', buildAugmentedPath()) ?? 'docker',
1943
+ gitPath: searchPathFor('git', buildAugmentedPath()) ?? 'git',
1944
+ // git starts `docker exec` itself when code moves in or out, so its PATH must find docker.
1945
+ hostEnvironment: { ...process.env, PATH: buildAugmentedPath() },
1946
+ // So the session list can say which registered project each space was made for.
1947
+ listProjectDirectories: async () => {
1948
+ const settings = await readSettingsFromDiskMigrated();
1949
+ return sanitizeProjects(settings?.projects || []).map((project) => project.path);
1950
+ },
1951
+ });
1952
+ const startupSettings = await readSettingsFromDiskMigrated().catch(() => null);
1953
+ if (startupSettings?.isolatedSpacesEnabled === true) {
1954
+ try {
1955
+ spacesHost = buildSpacesHost();
1956
+ } catch (error) {
1957
+ // The feature is absent then, and the rest of the server starts as with the switch off.
1958
+ console.error(`[spaces] isolated spaces are unavailable this start: ${error?.code ?? ''} ${error?.message ?? error}`.trim());
1959
+ spacesHost = null;
1960
+ }
1961
+ }
1962
+
1849
1963
  const app = express();
1850
1964
  const serverStartedAt = new Date().toISOString();
1851
1965
  const packagedClientOrigins = new Set([
@@ -1903,7 +2017,7 @@ async function main(options = {}) {
1903
2017
  const bootstrapResult = bootstrapRuntime.setupBaseRoutes(app, {
1904
2018
  process,
1905
2019
  openchamberVersion: OPENCHAMBER_VERSION,
1906
- runtimeName: process.env.OPENCHAMBER_RUNTIME || 'web',
2020
+ runtimeName,
1907
2021
  serverStartedAt,
1908
2022
  gracefulShutdown,
1909
2023
  getHealthSnapshot: () => {
@@ -1997,6 +2111,7 @@ async function main(options = {}) {
1997
2111
  isUiVisible,
1998
2112
  getUiNotificationClients: () => uiNotificationClients,
1999
2113
  writeSseEvent,
2114
+ pluginNotificationEmitter,
2000
2115
  sessionRuntime,
2001
2116
  setPushInitialized,
2002
2117
  fs,
@@ -2012,8 +2127,35 @@ async function main(options = {}) {
2012
2127
  setAutoAcceptSession,
2013
2128
  agentToolRuntime,
2014
2129
  desktopUpdater,
2130
+ skipBodyParsing: (req) => spacesHost?.skipsBodyParsing(req) === true,
2015
2131
  });
2016
2132
  uiAuthController = bootstrapResult.uiAuthController;
2133
+ // After the API auth gate, before every route that reads a directory, before the OpenCode proxy.
2134
+ // The slot is mounted once and reads the host at call time, so the switch can turn the feature
2135
+ // on and off live: with no host it passes every request on and no upgrade is taken.
2136
+ app.use((req, res, next) => (spacesHost ? spacesHost.middleware(req, res, next) : next()));
2137
+ server.on('upgrade', (...args) => { spacesHost?.upgradeHandler(...args); });
2138
+ const startSpacesHost = (host) => {
2139
+ host.prepareUpgrades({ uiAuthController, isRequestOriginAllowed });
2140
+ // Every space's events join the host's hub, and the host asks each space for its live status.
2141
+ void host.startEvents(globalMessageStreamHub).catch((error) => {
2142
+ console.warn(`[spaces] could not follow the spaces: ${error?.message ?? error}`);
2143
+ });
2144
+ };
2145
+ if (spacesHost) startSpacesHost(spacesHost);
2146
+ const spacesSwitch = createSwitchController({
2147
+ getHost: () => spacesHost,
2148
+ setHost: (host) => { spacesHost = host; },
2149
+ buildHost: buildSpacesHost,
2150
+ startHost: startSpacesHost,
2151
+ persist: (enabled) => persistSettings({ isolatedSpacesEnabled: enabled }),
2152
+ });
2153
+ registerSpaceRoutes(app, {
2154
+ getJourney: () => spacesHost?.journey ?? null,
2155
+ getPlaces: () => spacesHost?.places() ?? [],
2156
+ readSwitch: spacesSwitch.readSwitch,
2157
+ setSwitch: spacesSwitch.setSwitch,
2158
+ });
2017
2159
  realtimeProxyRuntime = attachRealtimeProxy({
2018
2160
  app,
2019
2161
  server,
@@ -2107,6 +2249,7 @@ async function main(options = {}) {
2107
2249
  guestSurfaceRuntime?.endForGuest(event.guestId);
2108
2250
  return browserControlRouter.handleGuestDeactivated(event);
2109
2251
  },
2252
+ surfaceViewerHeaders: (guestId, viewerId) => guestSurfaceRuntime?.viewerHeaders(guestId, viewerId) ?? null,
2110
2253
  builtInExtensionsDir: options.builtInExtensionsDir,
2111
2254
  openchamberUserConfigRoot: OPENCHAMBER_USER_CONFIG_ROOT,
2112
2255
  managedChatsRoot: OPENCHAMBER_CHATS_DIR,
@@ -2199,7 +2342,6 @@ async function main(options = {}) {
2199
2342
  terminalRebindWindowMs: TERMINAL_INPUT_WS_REBIND_WINDOW_MS,
2200
2343
  terminalMaxRebindsPerWindow: TERMINAL_INPUT_WS_MAX_REBINDS_PER_WINDOW,
2201
2344
  setupProxy,
2202
- scheduleOpenCodeApiDetection,
2203
2345
  bootstrapOpenCodeAtStartup,
2204
2346
  triggerHealthCheck,
2205
2347
  staticRoutesRuntime,
@@ -2264,10 +2406,12 @@ async function main(options = {}) {
2264
2406
  scheduledTasks: scheduledTasksRuntime.getStatus(),
2265
2407
  }),
2266
2408
  isReady: () => isOpenCodeReady,
2267
- getManagedOpenCodePreflight: () => openCodeLifecycleRuntime.getManagedOpenCodePreflight(),
2409
+ getManagedOpenCodePreflight: () => ownsOpenCodeProcess()
2410
+ ? openCodeLifecycleRuntime.getManagedOpenCodePreflight()
2411
+ : Promise.resolve(false),
2268
2412
  restartOpenCode: () => restartOpenCode(),
2269
2413
  getOpenCodeProcessInfo: () => {
2270
- const managed = Boolean((openCodeProcess || openCodePort) && !ENV_SKIP_OPENCODE_START && !isExternalOpenCode);
2414
+ const managed = Boolean(ownsOpenCodeProcess() && (openCodeProcess || openCodePort) && !ENV_SKIP_OPENCODE_START && !isExternalOpenCode);
2271
2415
  // Only ever expose pid/port for a server WE manage. The Electron-side
2272
2416
  // killer kills by port (lsof + kill -KILL), so returning a port we don't
2273
2417
  // own — e.g. an external/desktop OpenCode on 4096 we attached to — would
@@ -11,6 +11,11 @@ the user can want independently:
11
11
  `agentControlToolEnabled` setting is not `false`.
12
12
  - `openchamber_web` — looking at and interacting with the page in OpenChamber's
13
13
  browser panel. Enabled while `agentWebToolEnabled` is not `false`.
14
+ - `openchamber_notify` — `notify.send`, a notification to the user through
15
+ `lib/notifications/emit-route.js` (same limits and rate window as
16
+ `POST /api/notifications/emit`). Off by default: enabled only while
17
+ `agentNotifyToolEnabled` is `true`, and the control service refuses the
18
+ action when the setting is off, so a stale plugin cannot keep paging.
14
19
 
15
20
  Both default to on, are toggled in Settings → General → OpenCode CLI, and take
16
21
  effect in the running OpenCode within a couple of seconds — OpenChamber rewrites