@vantran-se/openchamber-web 1.24.3 → 2.0.1

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 (367) hide show
  1. package/README.md +2 -8
  2. package/bin/lib/commands-connect-url.js +3 -8
  3. package/bin/lib/commands-tunnel.js +3 -0
  4. package/dist/assets/{CodeMirrorCommentWidgets-BjJHrzmU.js → CodeMirrorCommentWidgets-ntXIbhV2.js} +1 -1
  5. package/dist/assets/DiffView-CjxUovbM.js +4 -0
  6. package/dist/assets/FilesView-DFWD0RQR.js +17 -0
  7. package/dist/assets/{GitView-CDOJy5Ay.js → GitView-BWa7e5vw.js} +4 -4
  8. package/dist/assets/{GuestAttachDialog-D3S0Rjic.js → GuestAttachDialog-DdOuyZzp.js} +1 -1
  9. package/dist/assets/GuestSurfacePane-DDfzid3U.js +1 -0
  10. package/dist/assets/{JsonTreeView-D0C9nU7K.js → JsonTreeView-3sGwqexh.js} +1 -1
  11. package/dist/assets/LinearIssuesView-C6dpIdjw.js +1 -0
  12. package/dist/assets/MarkdownImageGallery-Q7YR_tl4.js +1 -0
  13. package/dist/assets/MarkdownRendererImpl-DxDV56bL.js +12 -0
  14. package/dist/assets/OnboardingScreen-rK4Stg__.js +1 -0
  15. package/dist/assets/{PatchDiff-DnEhjXcC.js → PatchDiff-DS-IMU1j.js} +1 -1
  16. package/dist/assets/{PierreDiffViewer-DyxZVVVR.js → PierreDiffViewer-DW23KWKZ.js} +1 -1
  17. package/dist/assets/PlanView-BCk_VobP.js +5 -0
  18. package/dist/assets/PluginPane-BPP1dbck.js +3 -0
  19. package/dist/assets/SettingsView-kMmrpn43.js +29 -0
  20. package/dist/assets/SettingsWindow-BpvD6IYQ.js +1 -0
  21. package/dist/assets/SubmoduleDiffSummary-BLttSS1X.js +1 -0
  22. package/dist/assets/ToolOutputDialog-CtqDqbyr.js +15 -0
  23. package/dist/assets/ToolPartDiffPreview-5FcKdyRy.js +8 -0
  24. package/dist/assets/{VirtualizedFileDiff-0pyr_mHY.js → VirtualizedFileDiff-C763hNCR.js} +4 -4
  25. package/dist/assets/{WalkthroughView-yNj4nOP5.js → WalkthroughView-lap5btCF.js} +2 -2
  26. package/dist/assets/appThemeRegistry-u4zw1l0B.js +1 -0
  27. package/dist/assets/{commitAndPush-DvJKa3Ks.js → commitAndPush-CwGJfJG-.js} +1 -1
  28. package/dist/assets/{computeVirtualFileMetrics-DxTBMuu-.js → computeVirtualFileMetrics-DW5jjX4n.js} +1 -1
  29. package/dist/assets/de-CndEg67T.js +10 -0
  30. package/dist/assets/es-DglvPlaC.js +16 -0
  31. package/dist/assets/fr-DejPCpbS.js +14 -0
  32. package/dist/assets/index-C2r64QfD.js +1 -0
  33. package/dist/assets/index-CLcM8HKx.css +1 -0
  34. package/dist/assets/index-CvLgnuNI.js +41 -0
  35. package/dist/assets/{isDiffPlainText-D7FkBMP6.js → isDiffPlainText-D47a6hMP.js} +1 -1
  36. package/dist/assets/{isFilePlainText-CL2vsrIZ.js → isFilePlainText-B-mGgeKl.js} +1 -1
  37. package/dist/assets/ja-CVpQawCd.js +16 -0
  38. package/dist/assets/ko-Bw446h9j.js +16 -0
  39. package/dist/assets/main-Bz4WWXbr.js +2 -0
  40. package/dist/assets/main-oreqDsOV.js +896 -0
  41. package/dist/assets/{markdownCore-Dh7WEsdM.js → markdownCore-D2e5sp9y.js} +4 -4
  42. package/dist/assets/miniChat-D1v2gsBI.js +2 -0
  43. package/dist/assets/mobile-DBj3lCf2.js +2 -0
  44. package/dist/assets/{patchFileDiff-Bsba8KFp.js → patchFileDiff-BpeUOV91.js} +3 -3
  45. package/dist/assets/pl-BYNlPdhm.js +16 -0
  46. package/dist/assets/pt-BR-CL-Yfv9H.js +16 -0
  47. package/dist/assets/renderElectronMiniChatApp-DVtfEB2z.js +2 -0
  48. package/dist/assets/renderMobileApp-DFp9-xbG.js +25 -0
  49. package/dist/assets/runtimeEndpointReset-CsIpUep1.js +27 -0
  50. package/dist/assets/scheduledTasksApi-B5OHVpPM.js +1 -0
  51. package/dist/assets/{shikiHighlight-8_3csmr6.js → shikiHighlight-DNm2Z_Jc.js} +1 -1
  52. package/dist/assets/{textMateThemeFromAppTheme-CHo2fCgW.js → textMateThemeFromAppTheme-DGF2kG7g.js} +1 -1
  53. package/dist/assets/tr-DzmbxLy-.js +7 -0
  54. package/dist/assets/uk-C7vQnwro.js +16 -0
  55. package/dist/assets/useAppFontEffects-Ci3FEaPf.js +7337 -0
  56. package/dist/assets/{useInlineCommentController-DkQ_gqpn.js → useInlineCommentController-CuUuakrt.js} +2 -2
  57. package/dist/assets/useRootScrollLock-CQIsyERq.js +2 -0
  58. package/dist/assets/{vendor-codemirror-language-data-CdzUmUyZ.js → vendor-codemirror-language-data-CQa7_1cr.js} +2 -2
  59. package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
  60. package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
  61. package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
  62. package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
  63. package/dist/assets/{web-components-B8IcVQAA.js → web-components-B6tKQyJN.js} +2 -2
  64. package/dist/assets/zh-CN-Cl0srS-z.js +16 -0
  65. package/dist/assets/zh-TW-DtII5bDb.js +16 -0
  66. package/dist/index.html +13 -7
  67. package/dist/mini-chat.html +5 -5
  68. package/dist/mobile.html +5 -5
  69. package/package.json +6 -5
  70. package/server/index.d.ts +1 -0
  71. package/server/index.js +246 -91
  72. package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
  73. package/server/lib/agent-tool/runtime.js +135 -54
  74. package/server/lib/agent-tool/runtime.test.js +155 -87
  75. package/server/lib/browser-control/DOCUMENTATION.md +37 -2
  76. package/server/lib/browser-control/provider.js +217 -0
  77. package/server/lib/browser-control/provider.test.js +251 -0
  78. package/server/lib/context-obligatory/runtime.js +61 -50
  79. package/server/lib/context-obligatory/runtime.test.js +62 -162
  80. package/server/lib/event-stream/DOCUMENTATION.md +12 -1
  81. package/server/lib/event-stream/delta-coalescer.js +0 -0
  82. package/server/lib/event-stream/delta-coalescer.test.js +68 -57
  83. package/server/lib/event-stream/directory-ws-bridge.js +1 -1
  84. package/server/lib/event-stream/global-hub.js +10 -1
  85. package/server/lib/event-stream/global-hub.test.js +27 -26
  86. package/server/lib/event-stream/index.js +6 -0
  87. package/server/lib/event-stream/protocol.js +26 -19
  88. package/server/lib/event-stream/protocol.test.js +18 -7
  89. package/server/lib/event-stream/rebind.test.js +5 -5
  90. package/server/lib/event-stream/runtime.test.js +17 -15
  91. package/server/lib/event-stream/translate-v2.js +313 -0
  92. package/server/lib/event-stream/translate-v2.test.js +209 -0
  93. package/server/lib/event-stream/upstream-reader.test.js +16 -16
  94. package/server/lib/fs/DOCUMENTATION.md +4 -2
  95. package/server/lib/fs/byte-range.js +35 -0
  96. package/server/lib/fs/byte-range.test.js +31 -0
  97. package/server/lib/fs/routes.js +47 -42
  98. package/server/lib/fs/routes.test.js +123 -18
  99. package/server/lib/git/service.js +12 -0
  100. package/server/lib/github/DOCUMENTATION.md +1 -1
  101. package/server/lib/github/pr-status.js +22 -2
  102. package/server/lib/github/pr-status.test.js +27 -14
  103. package/server/lib/guests/DOCUMENTATION.md +13 -0
  104. package/server/lib/guests/catalog.js +12 -0
  105. package/server/lib/guests/catalog.test.js +33 -0
  106. package/server/lib/guests/routes.js +10 -1
  107. package/server/lib/guests/service.js +192 -14
  108. package/server/lib/guests/service.test.js +201 -1
  109. package/server/lib/guests/surface.js +630 -0
  110. package/server/lib/guests/surface.test.js +486 -0
  111. package/server/lib/markdown-image-grants/routes.js +18 -5
  112. package/server/lib/markdown-image-grants/routes.test.js +9 -6
  113. package/server/lib/message-queue/DOCUMENTATION.md +33 -22
  114. package/server/lib/message-queue/runtime.js +155 -100
  115. package/server/lib/message-queue/runtime.test.js +168 -82
  116. package/server/lib/notifications/DOCUMENTATION.md +28 -0
  117. package/server/lib/notifications/control-stream.http.test.js +68 -0
  118. package/server/lib/notifications/emitter-runtime.js +15 -0
  119. package/server/lib/notifications/emitter-runtime.test.js +32 -0
  120. package/server/lib/notifications/push-runtime.js +5 -1
  121. package/server/lib/notifications/push-runtime.test.js +2 -2
  122. package/server/lib/notifications/routes.js +1 -1
  123. package/server/lib/notifications/runtime.js +115 -26
  124. package/server/lib/notifications/template-runtime.js +13 -15
  125. package/server/lib/notifications/template-runtime.test.js +16 -9
  126. package/server/lib/notifications/trigger-runtime.test.js +187 -0
  127. package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
  128. package/server/lib/openchamber-control/actions.js +3 -2
  129. package/server/lib/openchamber-control/error.js +1 -1
  130. package/server/lib/openchamber-control/file-open.js +59 -0
  131. package/server/lib/openchamber-control/file-open.test.js +57 -0
  132. package/server/lib/openchamber-control/routes.test.js +12 -0
  133. package/server/lib/openchamber-control/service.js +102 -49
  134. package/server/lib/openchamber-control/service.test.js +84 -44
  135. package/server/lib/openchamber-sessions/archive-store.js +224 -0
  136. package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
  137. package/server/lib/openchamber-sessions/opencode-client.js +22 -0
  138. package/server/lib/openchamber-sessions/routes.js +398 -328
  139. package/server/lib/openchamber-sessions/routes.test.js +777 -740
  140. package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
  141. package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
  142. package/server/lib/opencode/DOCUMENTATION.md +449 -29
  143. package/server/lib/opencode/agents.js +233 -327
  144. package/server/lib/opencode/auth.js +41 -44
  145. package/server/lib/opencode/auth.test.js +92 -0
  146. package/server/lib/opencode/cli-upgrade.d.ts +4 -0
  147. package/server/lib/opencode/cli-upgrade.js +18 -0
  148. package/server/lib/opencode/cli-upgrade.test.js +36 -0
  149. package/server/lib/opencode/commands.js +164 -145
  150. package/server/lib/opencode/compatibility.d.ts +16 -0
  151. package/server/lib/opencode/compatibility.js +88 -0
  152. package/server/lib/opencode/compatibility.test.js +62 -0
  153. package/server/lib/opencode/config-entity-routes.js +58 -29
  154. package/server/lib/opencode/config-entity-v2.test.js +290 -0
  155. package/server/lib/opencode/config-mutation-response.js +17 -7
  156. package/server/lib/opencode/config-mutation-response.test.js +4 -7
  157. package/server/lib/opencode/config-paths.test.js +2 -2
  158. package/server/lib/opencode/config-v2.d.ts +183 -0
  159. package/server/lib/opencode/config-v2.js +801 -0
  160. package/server/lib/opencode/config-v2.test.js +466 -0
  161. package/server/lib/opencode/credential-db.js +144 -0
  162. package/server/lib/opencode/credential-db.test.js +104 -0
  163. package/server/lib/opencode/env-runtime.js +48 -9
  164. package/server/lib/opencode/env-runtime.test.js +45 -0
  165. package/server/lib/opencode/feature-routes-runtime.js +15 -5
  166. package/server/lib/opencode/lifecycle-process.test.js +7 -4
  167. package/server/lib/opencode/lifecycle.js +85 -30
  168. package/server/lib/opencode/lifecycle.test.js +80 -8
  169. package/server/lib/opencode/login-shell-env.js +27 -0
  170. package/server/lib/opencode/managed-config-file.js +119 -0
  171. package/server/lib/opencode/managed-config-file.test.js +152 -0
  172. package/server/lib/opencode/managed-plugin-config.js +33 -13
  173. package/server/lib/opencode/mcp.js +43 -149
  174. package/server/lib/opencode/network-runtime.js +6 -7
  175. package/server/lib/opencode/openchamber-routes.test.js +2 -5
  176. package/server/lib/opencode/plugin-routes.js +3 -3
  177. package/server/lib/opencode/plugin-routes.test.js +6 -24
  178. package/server/lib/opencode/plugins.js +91 -40
  179. package/server/lib/opencode/plugins.test.js +28 -15
  180. package/server/lib/opencode/providers.js +63 -83
  181. package/server/lib/opencode/providers.test.js +128 -59
  182. package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
  183. package/server/lib/opencode/proxy.js +179 -129
  184. package/server/lib/opencode/pwa-manifest-routes.js +4 -2
  185. package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
  186. package/server/lib/opencode/response-envelope.js +14 -0
  187. package/server/lib/opencode/response-envelope.test.js +23 -0
  188. package/server/lib/opencode/routes-upgrade.test.js +73 -116
  189. package/server/lib/opencode/routes.js +121 -428
  190. package/server/lib/opencode/server-utils-runtime.js +10 -4
  191. package/server/lib/opencode/session-activity.js +78 -0
  192. package/server/lib/opencode/session-runtime.js +21 -10
  193. package/server/lib/opencode/session-runtime.test.js +9 -9
  194. package/server/lib/opencode/settings-helpers.js +9 -9
  195. package/server/lib/opencode/settings-helpers.test.js +7 -12
  196. package/server/lib/opencode/settings-registry.json +6 -8
  197. package/server/lib/opencode/settings-runtime.js +18 -1
  198. package/server/lib/opencode/settings-runtime.test.js +31 -2
  199. package/server/lib/opencode/shared.js +54 -22
  200. package/server/lib/opencode/shared.test.js +8 -4
  201. package/server/lib/opencode/shutdown-runtime.js +54 -7
  202. package/server/lib/opencode/shutdown-runtime.test.js +224 -30
  203. package/server/lib/opencode/skill-routes.js +23 -24
  204. package/server/lib/opencode/skills.js +7 -5
  205. package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
  206. package/server/lib/opencode/upgrade-capability.test.js +1 -1
  207. package/server/lib/opencode/v1-migration-topup.js +288 -0
  208. package/server/lib/opencode/v1-migration-topup.test.js +266 -0
  209. package/server/lib/opencode/v2-install.d.ts +8 -0
  210. package/server/lib/opencode/v2-install.js +158 -0
  211. package/server/lib/opencode/v2-install.test.js +124 -0
  212. package/server/lib/opencode/watcher.js +10 -3
  213. package/server/lib/opencode/watcher.test.js +17 -21
  214. package/server/lib/opencode/websearch-config.js +66 -0
  215. package/server/lib/package-manager.js +55 -68
  216. package/server/lib/package-manager.test.js +241 -75
  217. package/server/lib/permission-auto-accept/runtime.js +38 -22
  218. package/server/lib/permission-auto-accept/runtime.test.js +39 -17
  219. package/server/lib/quota/DOCUMENTATION.md +4 -0
  220. package/server/lib/quota/providers/xai.js +4 -4
  221. package/server/lib/relay/tunnel-host.js +4 -1
  222. package/server/lib/relay/tunnel-host.test.js +7 -0
  223. package/server/lib/routing/DOCUMENTATION.md +52 -21
  224. package/server/lib/routing/defaults.js +23 -6
  225. package/server/lib/routing/jev.js +17 -7
  226. package/server/lib/routing/routes.http.test.js +68 -33
  227. package/server/lib/routing/routes.js +65 -22
  228. package/server/lib/routing/runtime.js +145 -48
  229. package/server/lib/routing/runtime.test.js +79 -55
  230. package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
  231. package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
  232. package/server/lib/scheduled-tasks/runtime.js +81 -74
  233. package/server/lib/scheduled-tasks/runtime.test.js +53 -1
  234. package/server/lib/session-assist/DOCUMENTATION.md +8 -3
  235. package/server/lib/session-assist/context.js +122 -46
  236. package/server/lib/session-assist/context.test.js +117 -49
  237. package/server/lib/session-assist/runtime.js +52 -22
  238. package/server/lib/session-assist/runtime.test.js +145 -173
  239. package/server/lib/session-goal/DOCUMENTATION.md +23 -15
  240. package/server/lib/session-goal/create.js +9 -12
  241. package/server/lib/session-goal/create.test.js +27 -28
  242. package/server/lib/session-goal/runtime.js +164 -77
  243. package/server/lib/session-goal/runtime.test.js +271 -519
  244. package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
  245. package/server/lib/session-knowledge/runtime.js +59 -41
  246. package/server/lib/session-knowledge/runtime.test.js +90 -20
  247. package/server/lib/small-model/DOCUMENTATION.md +154 -208
  248. package/server/lib/small-model/client.js +113 -0
  249. package/server/lib/small-model/index.js +296 -158
  250. package/server/lib/small-model/index.test.js +423 -256
  251. package/server/lib/small-model/routes.js +1 -1
  252. package/server/lib/spaces/DOCUMENTATION.md +807 -0
  253. package/server/lib/spaces/code-in-bait.js +185 -0
  254. package/server/lib/spaces/code-in.js +641 -0
  255. package/server/lib/spaces/code-in.test.js +1048 -0
  256. package/server/lib/spaces/errors.js +8 -0
  257. package/server/lib/spaces/exec-http.js +94 -0
  258. package/server/lib/spaces/gatekeeper-channel.js +202 -0
  259. package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
  260. package/server/lib/spaces/gatekeeper-program.cjs +914 -0
  261. package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
  262. package/server/lib/spaces/hardening.js +457 -0
  263. package/server/lib/spaces/hardening.test.js +368 -0
  264. package/server/lib/spaces/host-git.js +127 -0
  265. package/server/lib/spaces/host-git.test.js +137 -0
  266. package/server/lib/spaces/labels.js +234 -0
  267. package/server/lib/spaces/labels.test.js +219 -0
  268. package/server/lib/spaces/layout.js +184 -0
  269. package/server/lib/spaces/layout.test.js +30 -0
  270. package/server/lib/spaces/manager.js +82 -0
  271. package/server/lib/spaces/manager.test.js +114 -0
  272. package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
  273. package/server/lib/spaces/places/contract-suite.js +156 -0
  274. package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
  275. package/server/lib/spaces/places/contract.test.js +6 -0
  276. package/server/lib/spaces/places/docker-engine.js +126 -0
  277. package/server/lib/spaces/places/docker-live-support.js +202 -0
  278. package/server/lib/spaces/places/docker-tools.js +186 -0
  279. package/server/lib/spaces/places/docker.js +587 -0
  280. package/server/lib/spaces/places/docker.test.js +1610 -0
  281. package/server/lib/spaces/places/escape-suite.js +1018 -0
  282. package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
  283. package/server/lib/spaces/places/fake-docker.js +375 -0
  284. package/server/lib/spaces/places/memory-place.js +103 -0
  285. package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
  286. package/server/lib/spaces/places/registry.js +43 -0
  287. package/server/lib/spaces/places/registry.test.js +50 -0
  288. package/server/lib/spaces/places/server.docker.live.test.js +225 -0
  289. package/server/lib/spaces/run-command.js +261 -0
  290. package/server/lib/spaces/run-command.test.js +396 -0
  291. package/server/lib/spaces/space-server.js +190 -0
  292. package/server/lib/spaces/space-server.test.js +335 -0
  293. package/server/lib/spaces/tools-filler.js +82 -0
  294. package/server/lib/spaces/tools-filler.test.js +148 -0
  295. package/server/lib/spaces/tools-pack.js +49 -0
  296. package/server/lib/spaces/tools-pack.test.js +57 -0
  297. package/server/lib/spaces/tools.js +138 -0
  298. package/server/lib/spaces/tools.test.js +153 -0
  299. package/server/lib/tts/openai-sdk.js +11 -0
  300. package/server/lib/tts/service.js +7 -5
  301. package/server/lib/tts/stt.js +3 -2
  302. package/server/lib/tunnels/DOCUMENTATION.md +2 -0
  303. package/server/lib/tunnels/routes.js +7 -0
  304. package/server/lib/tunnels/routes.test.js +55 -0
  305. package/server/lib/ui-auth/ui-auth.js +38 -2
  306. package/server/lib/ui-auth/ui-auth.test.js +42 -0
  307. package/server/lib/ui-auth/ui-passkeys.js +12 -10
  308. package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
  309. package/server/lib/walkthrough/pull-request.js +61 -8
  310. package/server/lib/walkthrough/pull-request.test.js +54 -1
  311. package/server/lib/walkthrough/routes.js +20 -0
  312. package/server/lib/walkthrough/routes.test.js +19 -0
  313. package/server/opencode-proxy.test.js +190 -166
  314. package/server/opencode-worktree-readiness.test.js +5 -4
  315. package/dist/assets/DiffView-COVQhWGd.js +0 -4
  316. package/dist/assets/FilesView-B1V5JxFX.js +0 -13
  317. package/dist/assets/LinearIssuesView-D7FKg1XJ.js +0 -1
  318. package/dist/assets/MarkdownImageGallery-BrFjAVEf.js +0 -1
  319. package/dist/assets/MarkdownRendererImpl-DWepTafh.js +0 -12
  320. package/dist/assets/OnboardingScreen-Dyxxiqdt.js +0 -1
  321. package/dist/assets/PlanView-LW2z_EAP.js +0 -5
  322. package/dist/assets/PluginPane-B4tKno2h.js +0 -3
  323. package/dist/assets/SettingsView-DEaMulN9.js +0 -29
  324. package/dist/assets/SettingsWindow-FLDJXOgQ.js +0 -1
  325. package/dist/assets/SubmoduleDiffSummary-CvVwD-Vi.js +0 -1
  326. package/dist/assets/ToolOutputDialog-BiP0x2Rw.js +0 -15
  327. package/dist/assets/ToolPartDiffPreview-CkR4msKP.js +0 -8
  328. package/dist/assets/appThemeRegistry-BuUKhbYQ.js +0 -1
  329. package/dist/assets/de-DgxgFyGG.js +0 -10
  330. package/dist/assets/es-BFsZj5F_.js +0 -16
  331. package/dist/assets/fr-jdZ5KKQi.js +0 -14
  332. package/dist/assets/index-CKHjPKbq.js +0 -1
  333. package/dist/assets/index-Cbci4i1W.js +0 -40
  334. package/dist/assets/index-DqaMWNrC.css +0 -1
  335. package/dist/assets/ja-C5kKrhO4.js +0 -16
  336. package/dist/assets/ko-DXDTT_0O.js +0 -16
  337. package/dist/assets/main-CZALtVek.js +0 -896
  338. package/dist/assets/main-CgXMBNvG.js +0 -2
  339. package/dist/assets/miniChat-CwTmDixi.js +0 -2
  340. package/dist/assets/mobile-CLf2vCtO.js +0 -2
  341. package/dist/assets/pl-D-7x4Y8Q.js +0 -16
  342. package/dist/assets/pt-BR-CSBZrHa8.js +0 -16
  343. package/dist/assets/renderElectronMiniChatApp-C5gAFBk0.js +0 -2
  344. package/dist/assets/renderMobileApp-V4hYd32H.js +0 -25
  345. package/dist/assets/runtimeEndpointReset-DhlHCn-2.js +0 -25
  346. package/dist/assets/scheduledTasksApi-DzjbLd8P.js +0 -1
  347. package/dist/assets/tr-Dr-oJ22L.js +0 -7
  348. package/dist/assets/uk-9Nx83UcI.js +0 -16
  349. package/dist/assets/useAppFontEffects-khZXpFL_.js +0 -7333
  350. package/dist/assets/useRootScrollLock-DdvHf9Pt.js +0 -2
  351. package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
  352. package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
  353. package/dist/assets/zh-CN-Bxy_Hj3r.js +0 -16
  354. package/dist/assets/zh-TW-0BB4gpHM.js +0 -16
  355. package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
  356. package/server/lib/routing/feature-flag.js +0 -11
  357. package/server/lib/small-model/call.js +0 -851
  358. package/server/lib/small-model/call.test.js +0 -1292
  359. package/server/lib/small-model/catalog.js +0 -13
  360. package/server/lib/small-model/resolve.js +0 -131
  361. package/server/lib/small-model/resolve.test.js +0 -197
  362. package/server/lib/small-model/runtime-providers.js +0 -164
  363. package/server/lib/small-model/runtime-providers.test.js +0 -118
  364. package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
  365. package/server/lib/system-prompt/runtime.js +0 -49
  366. package/server/lib/system-prompt/runtime.test.js +0 -76
  367. package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
@@ -22,6 +22,8 @@ export const createServerUtilsRuntime = (dependencies) => {
22
22
  setOpenCodeNotReadySince,
23
23
  clearLastOpenCodeError,
24
24
  getLoginShellPath,
25
+ getArchivedSessions = null,
26
+ getStoredSessionMetadata = null,
25
27
  } = dependencies;
26
28
 
27
29
  const setOpenCodePort = (port) => {
@@ -191,16 +193,18 @@ export const createServerUtilsRuntime = (dependencies) => {
191
193
  throw new Error(`Failed to fetch ${invalidMessage} (status ${response.status})`);
192
194
  }
193
195
 
194
- const payload = await response.json().catch(() => null);
196
+ // OpenCode 2.x answers `/api/*` with `{ location, data }`.
197
+ const body = await response.json().catch(() => null);
198
+ const payload = Array.isArray(body) ? body : body?.data;
195
199
  if (!Array.isArray(payload)) {
196
200
  throw new Error(`Invalid ${invalidMessage} payload from OpenCode`);
197
201
  }
198
202
  return payload;
199
203
  };
200
204
 
201
- const fetchAgentsSnapshot = () => fetchArraySnapshot('/agent', 'agents snapshot');
202
- const fetchProvidersSnapshot = () => fetchArraySnapshot('/provider', 'providers snapshot');
203
- const fetchModelsSnapshot = () => fetchArraySnapshot('/model', 'models snapshot');
205
+ const fetchAgentsSnapshot = () => fetchArraySnapshot('/api/agent', 'agents snapshot');
206
+ const fetchProvidersSnapshot = () => fetchArraySnapshot('/api/provider', 'providers snapshot');
207
+ const fetchModelsSnapshot = () => fetchArraySnapshot('/api/model', 'models snapshot');
204
208
 
205
209
  const setupProxy = (app) => {
206
210
  registerOpenCodeProxy(app, {
@@ -215,6 +219,8 @@ export const createServerUtilsRuntime = (dependencies) => {
215
219
  ensureOpenCodeApiPrefix,
216
220
  getSseUpstreamStallTimeoutMs: getUpstreamStallTimeoutMs,
217
221
  getUiNotificationClients,
222
+ getArchivedSessions,
223
+ getStoredSessionMetadata,
218
224
  });
219
225
  };
220
226
 
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Whether a session's turn has really ended.
3
+ *
4
+ * A parent session goes idle while a background subagent keeps working in a
5
+ * child session. When the child finishes, OpenCode delivers its result to the
6
+ * parent, which runs again and goes idle a second time. So the parent's first
7
+ * idle is a pause, not the end of the turn: anything that treats idle as
8
+ * "done" (goal audits, ready notifications) has to check the children too.
9
+ *
10
+ * Every read answers `null` when OpenCode could not be asked: "could not look"
11
+ * must never pass for "nothing is running".
12
+ */
13
+
14
+ import { unwrapOpenCodeResponse } from './response-envelope.js';
15
+
16
+ const CHILDREN_PAGE_SIZE = 50;
17
+ const CHILDREN_MAX_PAGES = 8;
18
+
19
+ export const createSessionActivityProbe = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, timeoutMs = 10_000, fetchImpl = fetch }) => {
20
+ const openCodeGet = async (fetchPath, query) => {
21
+ const base = buildOpenCodeUrl(fetchPath, '');
22
+ const search = new URLSearchParams(query || {}).toString();
23
+ const response = await fetchImpl(search ? `${base}?${search}` : base, {
24
+ method: 'GET',
25
+ headers: { Accept: 'application/json', ...getOpenCodeAuthHeaders() },
26
+ signal: AbortSignal.timeout(timeoutMs),
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(`OpenCode GET ${fetchPath} failed with ${response.status}`);
30
+ }
31
+ return unwrapOpenCodeResponse(await response.json().catch(() => null));
32
+ };
33
+
34
+ /**
35
+ * `/api/session/active` is global and lists only the sessions running right
36
+ * now (`{ data: { [id]: status } }`), so an absent id is idle.
37
+ */
38
+ const fetchActiveSessionStatuses = async () => {
39
+ const statuses = await openCodeGet('/api/session/active').catch(() => null);
40
+ return statuses && typeof statuses === 'object' && !Array.isArray(statuses) ? statuses : null;
41
+ };
42
+
43
+ /**
44
+ * v2 has no `/session/{id}/children`; `GET /api/session?parentID=` lists a
45
+ * parent's subagent sessions, newest first, in cursor pages. Later pages
46
+ * carry the filter inside the cursor, so only the cursor and the limit
47
+ * travel. A parent with more subagents than this walks is unknown (`null`).
48
+ */
49
+ const fetchChildSessionIds = async (sessionId) => {
50
+ const ids = [];
51
+ const seenCursors = new Set();
52
+ let cursor;
53
+ for (let pageNumber = 0; pageNumber < CHILDREN_MAX_PAGES; pageNumber += 1) {
54
+ const page = await openCodeGet('/api/session', {
55
+ limit: String(CHILDREN_PAGE_SIZE),
56
+ ...(cursor ? { cursor } : { parentID: sessionId }),
57
+ }).catch(() => null);
58
+ if (!Array.isArray(page?.data)) return null;
59
+ for (const child of page.data) {
60
+ if (typeof child?.id === 'string' && child.id) ids.push(child.id);
61
+ }
62
+ const next = page.cursor?.next;
63
+ if (!next || page.data.length === 0 || seenCursors.has(next)) return ids;
64
+ seenCursors.add(next);
65
+ cursor = next;
66
+ }
67
+ return null;
68
+ };
69
+
70
+ /** True when a subagent of the session is still running; null when unknown. */
71
+ const hasWorkingChildren = async (sessionId, statuses) => {
72
+ const children = await fetchChildSessionIds(sessionId);
73
+ if (!children) return null;
74
+ return children.some((id) => Boolean(statuses[id]));
75
+ };
76
+
77
+ return { fetchActiveSessionStatuses, fetchChildSessionIds, hasWorkingChildren };
78
+ };
@@ -45,10 +45,10 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
45
45
  const sessionActivityCooldowns = new Map();
46
46
  const sessionStates = new Map();
47
47
  const sessionAttentionStates = new Map();
48
- // Pending permission and question requests per session, kept from the same
48
+ // Pending permission requests and forms per session, kept from the same
49
49
  // upstream stream. Clients that do not initialize a directory cannot read
50
- // its pending list from OpenCode (that read creates an instance), so this
51
- // map is their seed. Entries live until the matching reply, the session's
50
+ // its pending list from OpenCode (that read creates a location), so this map
51
+ // is their seed. Entries live until the matching reply, the session's
52
52
  // deletion, or an OpenCode restart, which drops every pending request.
53
53
  const pendingRequestsBySession = new Map();
54
54
  let activeSessionCount = 0;
@@ -56,7 +56,7 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
56
56
  const getOrCreatePendingRequests = (sessionId) => {
57
57
  let entry = pendingRequestsBySession.get(sessionId);
58
58
  if (!entry) {
59
- entry = { permissions: new Map(), questions: new Map() };
59
+ entry = { permissions: new Map(), forms: new Map() };
60
60
  pendingRequestsBySession.set(sessionId, entry);
61
61
  }
62
62
  return entry;
@@ -69,25 +69,36 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
69
69
  // pending request of that kind we can still vouch for.
70
70
  if (requestId) entry[kind].delete(requestId);
71
71
  else entry[kind].clear();
72
- if (entry.permissions.size === 0 && entry.questions.size === 0) pendingRequestsBySession.delete(sessionId);
72
+ if (entry.permissions.size === 0 && entry.forms.size === 0) pendingRequestsBySession.delete(sessionId);
73
73
  };
74
74
 
75
75
  const processBlockingRequestPayload = (payload) => {
76
76
  if (!payload || typeof payload.type !== 'string') return;
77
77
  const properties = payload.properties && typeof payload.properties === 'object' ? payload.properties : {};
78
- if (payload.type === 'permission.asked' || payload.type === 'question.asked') {
78
+ if (payload.type === 'permission.asked') {
79
79
  const sessionId = readRequestId(properties.sessionID);
80
80
  const requestId = readRequestId(properties.id);
81
81
  if (!sessionId || !requestId) return;
82
- getOrCreatePendingRequests(sessionId)[payload.type === 'permission.asked' ? 'permissions' : 'questions'].set(requestId, properties);
82
+ getOrCreatePendingRequests(sessionId).permissions.set(requestId, properties);
83
+ return;
84
+ }
85
+ // v2 replaced the question tool with forms; the request itself rides in
86
+ // `properties.form`, so that object is what clients receive.
87
+ if (payload.type === 'form.created') {
88
+ const form = properties.form && typeof properties.form === 'object' ? properties.form : null;
89
+ if (!form) return;
90
+ const sessionId = readRequestId(form.sessionID) || readRequestId(properties.sessionID);
91
+ const requestId = readRequestId(form.id);
92
+ if (!sessionId || !requestId) return;
93
+ getOrCreatePendingRequests(sessionId).forms.set(requestId, form);
83
94
  return;
84
95
  }
85
96
  if (payload.type === 'permission.replied') {
86
97
  settlePendingRequest('permissions', readRequestId(properties.sessionID), readRequestId(properties.requestID));
87
98
  return;
88
99
  }
89
- if (payload.type === 'question.replied' || payload.type === 'question.rejected') {
90
- settlePendingRequest('questions', readRequestId(properties.sessionID), readRequestId(properties.requestID));
100
+ if (payload.type === 'form.settled') {
101
+ settlePendingRequest('forms', readRequestId(properties.sessionID), readRequestId(properties.formID));
91
102
  return;
92
103
  }
93
104
  if (payload.type === 'session.deleted') {
@@ -102,7 +113,7 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
102
113
  for (const [sessionId, entry] of pendingRequestsBySession) {
103
114
  result[sessionId] = {
104
115
  permissions: [...entry.permissions.values()],
105
- questions: [...entry.questions.values()],
116
+ forms: [...entry.forms.values()],
106
117
  };
107
118
  }
108
119
  return result;
@@ -12,26 +12,26 @@ describe('session runtime', () => {
12
12
  runtimes.length = 0;
13
13
  });
14
14
 
15
- it('keeps pending permission and question requests until they are answered', () => {
15
+ it('keeps pending permission requests and forms until they are answered', () => {
16
16
  const runtime = createSessionRuntime({
17
17
  writeSseEvent() {},
18
18
  getNotificationClients: () => new Set(),
19
19
  broadcastEvent: () => {},
20
20
  });
21
21
  runtimes.push(runtime);
22
- const permission = { id: 'perm-1', sessionID: 'session-1', permission: 'bash', patterns: ['rm *'], metadata: {}, always: [] };
23
- const question = { id: 'q-1', sessionID: 'session-2', questions: [{ header: 'Pick', question: 'Which?', options: [] }] };
22
+ const permission = { id: 'perm-1', sessionID: 'session-1', action: 'bash', resources: ['rm *'], metadata: {} };
23
+ const form = { id: 'form-1', sessionID: 'session-2', title: 'Pick one', fields: {} };
24
24
 
25
25
  runtime.processOpenCodeSsePayload({ type: 'permission.asked', properties: permission });
26
26
  runtime.processOpenCodeSsePayload({ type: 'permission.asked', properties: permission });
27
- runtime.processOpenCodeSsePayload({ type: 'question.asked', properties: question });
27
+ runtime.processOpenCodeSsePayload({ type: 'form.created', properties: { sessionID: 'session-2', form } });
28
28
  expect(runtime.getPendingBlockingRequestsSnapshot()).toEqual({
29
- 'session-1': { permissions: [permission], questions: [] },
30
- 'session-2': { permissions: [], questions: [question] },
29
+ 'session-1': { permissions: [permission], forms: [] },
30
+ 'session-2': { permissions: [], forms: [form] },
31
31
  });
32
32
 
33
33
  runtime.processOpenCodeSsePayload({ type: 'permission.replied', properties: { sessionID: 'session-1', requestID: 'perm-1' } });
34
- runtime.processOpenCodeSsePayload({ type: 'question.rejected', properties: { sessionID: 'session-2' } });
34
+ runtime.processOpenCodeSsePayload({ type: 'form.settled', properties: { sessionID: 'session-2' } });
35
35
  expect(runtime.getPendingBlockingRequestsSnapshot()).toEqual({});
36
36
  });
37
37
 
@@ -43,12 +43,12 @@ describe('session runtime', () => {
43
43
  });
44
44
  runtimes.push(runtime);
45
45
  const ask = (sessionID, id) => runtime.processOpenCodeSsePayload({
46
- type: 'permission.asked', properties: { id, sessionID, permission: 'edit', patterns: [], metadata: {}, always: [] },
46
+ type: 'permission.asked', properties: { id, sessionID, action: 'edit', resources: [], metadata: {} },
47
47
  });
48
48
 
49
49
  ask('session-1', 'perm-1');
50
50
  ask('session-2', 'perm-2');
51
- runtime.processOpenCodeSsePayload({ type: 'session.deleted', properties: { info: { id: 'session-1' } } });
51
+ runtime.processOpenCodeSsePayload({ type: 'session.deleted', properties: { sessionID: 'session-1', info: { id: 'session-1' } } });
52
52
  expect(Object.keys(runtime.getPendingBlockingRequestsSnapshot())).toEqual(['session-2']);
53
53
 
54
54
  runtime.interruptBusySessionsAfterRestart();
@@ -1,7 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
 
3
3
  import { isAgentMemoryFeatureAvailable } from '../agent-memory/feature-flag.js';
4
- import { isRoutingFeatureAvailable } from '../routing/feature-flag.js';
5
4
 
6
5
  // Generated from packages/ui/src/lib/settings/registry.ts by
7
6
  // `bun run settings-registry:generate`; `registry.test.ts` fails when stale.
@@ -64,7 +63,7 @@ export const createSettingsHelpers = (dependencies) => {
64
63
  const MOBILE_KEYBOARD_MODE_VALUES = new Set(['native', 'resize-content']);
65
64
  const TERMINAL_SHELL_VALUES = new Set(['auto', 'bash', 'zsh', 'sh', 'fish', 'pwsh', 'powershell', 'cmd', 'dash', 'ksh', 'nu']);
66
65
  const SIDEBAR_PROJECT_DISPLAY_MODE_VALUES = new Set(['all', 'single']);
67
- const SIDEBAR_SESSION_GROUPING_MODE_VALUES = new Set(['by-worktree', 'flat']);
66
+ const SIDEBAR_VIEW_MODE_VALUES = new Set(['projects', 'timeline']);
68
67
  const SIDEBAR_PROJECT_SORT_ORDER_VALUES = new Set(['manual', 'a-z', 'z-a', 'date-added', 'recent']);
69
68
  const HIDDEN_MODELS_MAX = 1024;
70
69
  const RECENT_EFFORTS_MAX_KEYS = 128;
@@ -297,8 +296,8 @@ export const createSettingsHelpers = (dependencies) => {
297
296
  if (SIDEBAR_PROJECT_DISPLAY_MODE_VALUES.has(candidate.sidebarProjectDisplayMode)) {
298
297
  result.sidebarProjectDisplayMode = candidate.sidebarProjectDisplayMode;
299
298
  }
300
- if (SIDEBAR_SESSION_GROUPING_MODE_VALUES.has(candidate.sidebarSessionGroupingMode)) {
301
- result.sidebarSessionGroupingMode = candidate.sidebarSessionGroupingMode;
299
+ if (SIDEBAR_VIEW_MODE_VALUES.has(candidate.sidebarViewMode)) {
300
+ result.sidebarViewMode = candidate.sidebarViewMode;
302
301
  }
303
302
  if (SIDEBAR_PROJECT_SORT_ORDER_VALUES.has(candidate.sidebarProjectSortOrder)) {
304
303
  result.sidebarProjectSortOrder = candidate.sidebarProjectSortOrder;
@@ -599,15 +598,15 @@ export const createSettingsHelpers = (dependencies) => {
599
598
  if (typeof candidate.agentWebToolEnabled === 'boolean') {
600
599
  result.agentWebToolEnabled = candidate.agentWebToolEnabled;
601
600
  }
601
+ if (typeof candidate.browserProvider === 'string' && candidate.browserProvider.trim()) {
602
+ result.browserProvider = candidate.browserProvider.trim();
603
+ }
602
604
  if (typeof candidate.agentControlToolEnabled === 'boolean') {
603
605
  result.agentControlToolEnabled = candidate.agentControlToolEnabled;
604
606
  }
605
607
  if (typeof candidate.agentMemoryToolEnabled === 'boolean') {
606
608
  result.agentMemoryToolEnabled = candidate.agentMemoryToolEnabled;
607
609
  }
608
- if (typeof candidate.optimizeSystemPrompt === 'boolean') {
609
- result.optimizeSystemPrompt = candidate.optimizeSystemPrompt;
610
- }
611
610
  if (typeof candidate.openCodeUpdateToastDismissedVersion === 'string') {
612
611
  const version = candidate.openCodeUpdateToastDismissedVersion.trim();
613
612
  result.openCodeUpdateToastDismissedVersion = version.slice(0, VERSION_STRING_MAX_LENGTH);
@@ -1014,8 +1013,9 @@ export const createSettingsHelpers = (dependencies) => {
1014
1013
  // Tells the client whether agent memory exists in this build at all, so
1015
1014
  // its settings row and panel tab can be absent rather than merely off.
1016
1015
  agentMemoryFeatureAvailable: isAgentMemoryFeatureAvailable(),
1017
- // Same idea for Jev routing: absent from the picker and Settings unless the build has it.
1018
- routingFeatureAvailable: isRoutingFeatureAvailable(),
1016
+ // Jev routing needs the OpenChamber server, so it is present here and
1017
+ // absent wherever this payload does not come from one (VS Code).
1018
+ routingFeatureAvailable: true,
1019
1019
  ...(pwaAppName ? { pwaAppName } : {}),
1020
1020
  pwaOrientation,
1021
1021
  mobileKeyboardMode,
@@ -175,18 +175,18 @@ describe('settings helpers', () => {
175
175
 
176
176
  expect(helpers.sanitizeSettingsUpdate({
177
177
  sidebarProjectDisplayMode: 'single',
178
- sidebarSessionGroupingMode: 'flat',
178
+ sidebarViewMode: 'timeline',
179
179
  sidebarProjectSortOrder: 'z-a',
180
180
  sidebarShowRecentSection: false,
181
181
  })).toEqual({
182
182
  sidebarProjectDisplayMode: 'single',
183
- sidebarSessionGroupingMode: 'flat',
183
+ sidebarViewMode: 'timeline',
184
184
  sidebarProjectSortOrder: 'z-a',
185
185
  sidebarShowRecentSection: false,
186
186
  });
187
187
  expect(helpers.sanitizeSettingsUpdate({
188
188
  sidebarProjectDisplayMode: 'grid',
189
- sidebarSessionGroupingMode: 'project',
189
+ sidebarViewMode: 'grid',
190
190
  sidebarProjectSortOrder: 'random',
191
191
  sidebarShowRecentSection: 'false',
192
192
  })).toEqual({});
@@ -667,9 +667,6 @@ describe('settings helpers', () => {
667
667
  it('persists only boolean system prompt optimization values', () => {
668
668
  const helpers = createTestHelpersWithRealSanitizers();
669
669
 
670
- expect(helpers.sanitizeSettingsUpdate({ optimizeSystemPrompt: true })).toEqual({ optimizeSystemPrompt: true });
671
- expect(helpers.sanitizeSettingsUpdate({ optimizeSystemPrompt: false })).toEqual({ optimizeSystemPrompt: false });
672
- expect(helpers.sanitizeSettingsUpdate({ optimizeSystemPrompt: 'true' })).toEqual({});
673
670
  });
674
671
 
675
672
  it('survives a full settings.json payload containing all four previously-dropped fields (regression)', () => {
@@ -767,13 +764,13 @@ describe('settings registry gate', () => {
767
764
  desktopLanAccessEnabled: true, desktopKeepAwakeEnabled: true, desktopMinimizeToTrayEnabled: true, desktopMacMenuBarEnabled: true,
768
765
  desktopUiPassword: 'secret', githubClientId: 'client', githubScopes: 'repo', skillCatalogs: [{ id: 'c', label: 'C', source: 'https://x' }],
769
766
  defaultGitIdentityId: 'global', permissionAutoAccept: { sessions: { s: true }, revision: 1 },
770
- agentControlToolEnabled: true, agentWebToolEnabled: true, agentMemoryToolEnabled: true, openCodeUpdateToastDismissedVersion: '1.0.0',
767
+ agentControlToolEnabled: true, agentWebToolEnabled: true, browserProvider: 'builtin', agentMemoryToolEnabled: true, openCodeUpdateToastDismissedVersion: '1.0.0',
771
768
  autoDeleteEnabled: true, autoDeleteAfterDays: 30, sessionRetentionOnlyArchived: false, sessionRetentionAction: 'archive', terminalShell: 'zsh', terminalLoginShells: ['zsh'],
772
769
  openInAppId: 'vscode', dictationEnabled: true, sttProvider: 'local', sttServerUrl: 'http://localhost:8001/v1', sttModel: 'm', sttLocalModel: 'm', sttLanguage: 'en',
773
770
  tunnelProvider: 'cloudflare', tunnelMode: 'quick', tunnelBootstrapTtlMs: 600000, tunnelSessionTtlMs: 86400000, managedLocalTunnelConfigPath: '/tmp/x',
774
771
  managedRemoteTunnelHostname: 'x.example', managedRemoteTunnelToken: 'token', managedRemoteTunnelPresets: [{ id: 'a', name: 'A', hostname: 'a.example' }],
775
772
  managedRemoteTunnelSelectedPresetId: 'a', managedRemoteTunnelPresetTokens: { a: 'token' },
776
- sidebarProjectDisplayMode: 'all', sidebarSessionGroupingMode: 'flat', sidebarProjectSortOrder: 'manual', sidebarShowRecentSection: true,
773
+ sidebarProjectDisplayMode: 'all', sidebarViewMode: 'timeline', sidebarProjectSortOrder: 'manual', sidebarShowRecentSection: true,
777
774
  workStatusPanelEnabled: true, workStatusHiddenSections: ['mcp'], workStatusHiddenSectionsExplicit: true, workStatusSectionOrder: ['mcp', 'session'],
778
775
  showReasoningTraces: true, streamingAutoFollowEnabled: true, collapsibleThinkingBlocks: true, showTextJustificationActivity: true,
779
776
  chatRenderMode: 'live', activityRenderMode: 'summary', mermaidRenderingMode: 'svg', userMessageRenderingMode: 'markdown', collapsibleUserMessages: true,
@@ -781,7 +778,7 @@ describe('settings registry gate', () => {
781
778
  codeBlockLineWrap: true, showTurnChangedFiles: true, showExpandedBashTools: true, showExpandedEditTools: true, toolJsonViewMode: 'raw',
782
779
  timeFormatPreference: '24h', weekStartPreference: 'monday', messageStreamTransport: 'ws', diffLayoutPreference: 'inline', diffWrapLines: true,
783
780
  gitChangesViewMode: 'tree', gitmojiEnabled: true, defaultFileViewerPreview: true, directoryShowHidden: true, filesViewShowGitignored: true,
784
- fileEditorKeymap: 'vim', autoSaveEnabled: true, autoCreateWorktree: true, sessionTabsEnabled: true, showOpenCodeRestartConfirm: true,
781
+ fileEditorKeymap: 'vim', autoSaveEnabled: true, autoCreateWorktree: true, sessionTabsEnabled: true,
785
782
  allowPromptingSubagentSessions: true, inputSpellcheckEnabled: true, enterToSend: true, enterToSendConfigured: true, persistChatDraft: true,
786
783
  largeTextPasteBehavior: 'attach', followUpBehavior: 'steer', queueModeEnabled: true, inputHistoryScope: 'global', inputHistoryLimit: 40,
787
784
  draftStarters: [{ type: 'command', name: 'plan-feature' }], draftStartersVisible: true, draftStartersCraftGoalAdded: true, draftStartersScheduleTaskAdded: true,
@@ -797,7 +794,7 @@ describe('settings registry gate', () => {
797
794
  notificationTemplates: { completion: { title: 't', message: 'm' } }, showOpenCodeUpdateNotifications: true, reportUsage: true,
798
795
  usageDisplayMode: 'usage', usageDropdownProviders: ['anthropic'], usageSelectedModels: { anthropic: ['claude'] }, usageCollapsedFamilies: { anthropic: ['f'] },
799
796
  usageExpandedFamilies: { anthropic: ['f'] }, usageModelGroups: { anthropic: { customGroups: [{ id: 'g', label: 'G', models: ['claude'], order: 0 }] } },
800
- globalBehaviorPrompt: 'Be brief.', responseStyleEnabled: true, responseStylePreset: 'concise', responseStyleCustomInstructions: 'x', optimizeSystemPrompt: true,
797
+ globalBehaviorPrompt: 'Be brief.', responseStyleEnabled: true, responseStylePreset: 'concise', responseStyleCustomInstructions: 'x',
801
798
  pwaAppName: 'OpenChamber', pwaOrientation: 'portrait', mobileKeyboardMode: 'native', desktopWindowControlsPosition: 'left', desktopWindowControlsStyle: 'classic',
802
799
  inputBarOffset: 10,
803
800
  };
@@ -879,7 +876,6 @@ describe('settings registry gate', () => {
879
876
  largeTextPasteBehavior: 'inline',
880
877
  fileEditorKeymap: 'vim',
881
878
  allowPromptingSubagentSessions: true,
882
- showOpenCodeRestartConfirm: false,
883
879
  codeBlockLineWrap: true,
884
880
  streamingAutoFollowEnabled: false,
885
881
  autoSaveEnabled: false,
@@ -890,7 +886,6 @@ describe('settings registry gate', () => {
890
886
  largeTextPasteBehavior: 'inline',
891
887
  fileEditorKeymap: 'vim',
892
888
  allowPromptingSubagentSessions: true,
893
- showOpenCodeRestartConfirm: false,
894
889
  codeBlockLineWrap: true,
895
890
  streamingAutoFollowEnabled: false,
896
891
  autoSaveEnabled: false,
@@ -120,6 +120,9 @@
120
120
  "agentWebToolEnabled": {
121
121
  "scope": "instance"
122
122
  },
123
+ "browserProvider": {
124
+ "scope": "instance"
125
+ },
123
126
  "agentMemoryToolEnabled": {
124
127
  "scope": "instance"
125
128
  },
@@ -212,8 +215,9 @@
212
215
  "sidebarProjectDisplayMode": {
213
216
  "scope": "profile"
214
217
  },
215
- "sidebarSessionGroupingMode": {
216
- "scope": "profile"
218
+ "sidebarViewMode": {
219
+ "scope": "profile",
220
+ "perSurface": true
217
221
  },
218
222
  "sidebarProjectSortOrder": {
219
223
  "scope": "profile"
@@ -341,9 +345,6 @@
341
345
  "vscode"
342
346
  ]
343
347
  },
344
- "showOpenCodeRestartConfirm": {
345
- "scope": "profile"
346
- },
347
348
  "allowPromptingSubagentSessions": {
348
349
  "scope": "profile"
349
350
  },
@@ -544,9 +545,6 @@
544
545
  "responseStyleCustomInstructions": {
545
546
  "scope": "profile"
546
547
  },
547
- "optimizeSystemPrompt": {
548
- "scope": "profile"
549
- },
550
548
  "pwaAppName": {
551
549
  "scope": "instance",
552
550
  "surfaces": [
@@ -40,6 +40,13 @@ const ensureNotificationTemplateShape = (templates) => {
40
40
  return { templates: next, changed };
41
41
  };
42
42
 
43
+ /** Settings that decide which OpenChamber plugins the managed OpenCode loads. */
44
+ const MANAGED_PLUGIN_SETTINGS_KEYS = new Set([
45
+ 'agentControlToolEnabled',
46
+ 'agentWebToolEnabled',
47
+ 'agentMemoryToolEnabled',
48
+ ]);
49
+
43
50
  export const createSettingsRuntime = (deps) => {
44
51
  const {
45
52
  fsPromises,
@@ -58,6 +65,7 @@ export const createSettingsRuntime = (deps) => {
58
65
  normalizeManagedRemoteTunnelPresetTokens,
59
66
  syncManagedRemoteTunnelConfigWithPresets,
60
67
  upsertManagedRemoteTunnelToken,
68
+ onManagedPluginSettingsChanged = async () => {},
61
69
  } = deps;
62
70
 
63
71
  let persistSettingsLock = Promise.resolve();
@@ -1110,7 +1118,16 @@ export const createSettingsRuntime = (deps) => {
1110
1118
  }
1111
1119
  }
1112
1120
 
1113
- await writeSettingsToDisk(next, { surface, changedKeys: Object.keys(sanitized) });
1121
+ const changedKeys = Object.keys(sanitized);
1122
+ await writeSettingsToDisk(next, { surface, changedKeys });
1123
+ // OpenChamber's own OpenCode plugins live in a config file OpenCode
1124
+ // watches, so flipping one of these switches takes effect in the running
1125
+ // process instead of waiting for a restart.
1126
+ if (changedKeys.some((key) => MANAGED_PLUGIN_SETTINGS_KEYS.has(key))) {
1127
+ await Promise.resolve(onManagedPluginSettingsChanged(next)).catch((error) => {
1128
+ console.warn('Failed to refresh the managed OpenCode config:', error?.message ?? error);
1129
+ });
1130
+ }
1114
1131
  return formatSettingsResponse(next);
1115
1132
  });
1116
1133
 
@@ -6,7 +6,10 @@ import path from 'path';
6
6
  import { createProjectIdFromPath, projectConfigFileStemOf } from '../projects/project-id.js';
7
7
  import { createSettingsRuntime } from './settings-runtime.js';
8
8
 
9
- const createRuntime = async ({ mergePersistedSettings = (_current, changes) => changes } = {}) => {
9
+ const createRuntime = async ({
10
+ mergePersistedSettings = (_current, changes) => changes,
11
+ onManagedPluginSettingsChanged = undefined,
12
+ } = {}) => {
10
13
  const tempRoot = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'oc-settings-runtime-'));
11
14
  const settingsFilePath = path.join(tempRoot, 'settings.json');
12
15
  const runtime = createSettingsRuntime({
@@ -26,6 +29,7 @@ const createRuntime = async ({ mergePersistedSettings = (_current, changes) => c
26
29
  normalizeManagedRemoteTunnelPresetTokens: (value) => value,
27
30
  syncManagedRemoteTunnelConfigWithPresets: async () => {},
28
31
  upsertManagedRemoteTunnelToken: async () => {},
32
+ onManagedPluginSettingsChanged,
29
33
  });
30
34
 
31
35
  return {
@@ -39,6 +43,31 @@ const createRuntime = async ({ mergePersistedSettings = (_current, changes) => c
39
43
  };
40
44
 
41
45
  describe('settings runtime', () => {
46
+ it('refreshes the managed OpenCode config only when a managed plugin setting changed', async () => {
47
+ const onManagedPluginSettingsChanged = vi.fn(async () => {});
48
+ const { runtime, cleanup } = await createRuntime({ onManagedPluginSettingsChanged });
49
+ try {
50
+ await runtime.persistSettings({ lightThemeId: 'flexoki-light' });
51
+ expect(onManagedPluginSettingsChanged).not.toHaveBeenCalled();
52
+
53
+ await runtime.persistSettings({ agentWebToolEnabled: false });
54
+ expect(onManagedPluginSettingsChanged).toHaveBeenCalledTimes(1);
55
+ } finally {
56
+ await cleanup();
57
+ }
58
+ });
59
+
60
+ it('still answers the settings write when the managed config refresh fails', async () => {
61
+ const onManagedPluginSettingsChanged = vi.fn(async () => { throw new Error('disk full'); });
62
+ const { runtime, cleanup } = await createRuntime({ onManagedPluginSettingsChanged });
63
+ try {
64
+ await expect(runtime.persistSettings({ agentMemoryToolEnabled: true }))
65
+ .resolves.toMatchObject({ agentMemoryToolEnabled: true });
66
+ } finally {
67
+ await cleanup();
68
+ }
69
+ });
70
+
42
71
  it('round-trips both archived-only retention states through instance settings', async () => {
43
72
  const { runtime, settingsFilePath, cleanup } = await createRuntime();
44
73
  try {
@@ -86,7 +115,7 @@ describe('settings runtime', () => {
86
115
  const { runtime, settingsFilePath, tempRoot, cleanup } = await createRuntime();
87
116
  const preferences = {
88
117
  sidebarProjectDisplayMode: 'single',
89
- sidebarSessionGroupingMode: 'flat',
118
+ sidebarViewMode: 'timeline',
90
119
  sidebarProjectSortOrder: 'date-added',
91
120
  sidebarShowRecentSection: false,
92
121
  };