@vantran-se/openchamber-web 1.24.4 → 2.0.2

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-B4a0JkUz.js → CodeMirrorCommentWidgets-B82Oye3V.js} +1 -1
  5. package/dist/assets/DiffView-DJLmWn6Z.js +4 -0
  6. package/dist/assets/FilesView-DjksmZRo.js +17 -0
  7. package/dist/assets/{GitView-Byu2DY7s.js → GitView-DiUsUIgr.js} +4 -4
  8. package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-CYwWYLr3.js} +1 -1
  9. package/dist/assets/GuestSurfacePane-CmctyRI4.js +1 -0
  10. package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-DOHHcbgJ.js} +1 -1
  11. package/dist/assets/LinearIssuesView-DaqnylcV.js +1 -0
  12. package/dist/assets/MarkdownImageGallery-HJNvWt7L.js +1 -0
  13. package/dist/assets/MarkdownRendererImpl-nDZRGg2I.js +12 -0
  14. package/dist/assets/OnboardingScreen-C2B5Bsof.js +1 -0
  15. package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-C4FLBjpt.js} +1 -1
  16. package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-C_1S9c9n.js} +1 -1
  17. package/dist/assets/PlanView-_eZvb-G5.js +5 -0
  18. package/dist/assets/PluginPane-sM8fcZIV.js +3 -0
  19. package/dist/assets/SettingsView-BRfr46hb.js +29 -0
  20. package/dist/assets/SettingsWindow-CcUDwQHg.js +1 -0
  21. package/dist/assets/SubmoduleDiffSummary-CrN1ZGwl.js +1 -0
  22. package/dist/assets/ToolOutputDialog-Cij4jI09.js +15 -0
  23. package/dist/assets/ToolPartDiffPreview-BXzwQ-JY.js +8 -0
  24. package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-DSWTLJNH.js} +4 -4
  25. package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-CpUUgY8H.js} +2 -2
  26. package/dist/assets/appThemeRegistry-B5tHTkR8.js +1 -0
  27. package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-D7D8qV8p.js} +1 -1
  28. package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-cq9uQ30i.js} +1 -1
  29. package/dist/assets/de-CUO1-KqF.js +10 -0
  30. package/dist/assets/es-BlbC6sGJ.js +16 -0
  31. package/dist/assets/fr-D2nwe_0W.js +14 -0
  32. package/dist/assets/index-BQOoYIOu.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-CO4e5J_S.js → isDiffPlainText-D0obJ2p8.js} +1 -1
  36. package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-DB3_rjyW.js} +1 -1
  37. package/dist/assets/ja-CA7vnDsZ.js +16 -0
  38. package/dist/assets/ko-Chy6a9l-.js +16 -0
  39. package/dist/assets/main-B9JFK3vF.js +896 -0
  40. package/dist/assets/main-Bhiqnrhd.js +2 -0
  41. package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-tfqOFaBd.js} +4 -4
  42. package/dist/assets/miniChat-B0UOL5zA.js +2 -0
  43. package/dist/assets/mobile-BJppFIEo.js +2 -0
  44. package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-Db6KuwCP.js} +3 -3
  45. package/dist/assets/pl-CTR5A_1P.js +16 -0
  46. package/dist/assets/pt-BR-BE1O1KEx.js +16 -0
  47. package/dist/assets/renderElectronMiniChatApp-Yz5sfoFh.js +2 -0
  48. package/dist/assets/renderMobileApp-QQ-yB8aY.js +25 -0
  49. package/dist/assets/runtimeEndpointReset-DKDdzRzm.js +27 -0
  50. package/dist/assets/scheduledTasksApi-C5sCuNqf.js +1 -0
  51. package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-xURoo5t_.js} +1 -1
  52. package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-BZLGuYTU.js} +1 -1
  53. package/dist/assets/tr-Bh5vK6cW.js +7 -0
  54. package/dist/assets/uk-CLDkZFy_.js +16 -0
  55. package/dist/assets/useAppFontEffects-BxrtCnfR.js +7337 -0
  56. package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CeXuR-SR.js} +2 -2
  57. package/dist/assets/useRootScrollLock-DgWxezc1.js +2 -0
  58. package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-DOuaQUZ0.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-DxX8bZ1o.js → web-components-63H1h21j.js} +2 -2
  64. package/dist/assets/zh-CN-DODgcmYj.js +16 -0
  65. package/dist/assets/zh-TW-ByNfIwLA.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-B2CD9wC8.js +0 -4
  316. package/dist/assets/FilesView-C4vMFclc.js +0 -13
  317. package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
  318. package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
  319. package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
  320. package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
  321. package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
  322. package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
  323. package/dist/assets/SettingsView-CSinuH-1.js +0 -29
  324. package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
  325. package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
  326. package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
  327. package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
  328. package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
  329. package/dist/assets/de-Bro6_zyl.js +0 -10
  330. package/dist/assets/es-Bb9XQZvd.js +0 -16
  331. package/dist/assets/fr-Dgfzn-IL.js +0 -14
  332. package/dist/assets/index-CtQNmqUL.js +0 -1
  333. package/dist/assets/index-DqaMWNrC.css +0 -1
  334. package/dist/assets/index-Dqc6USl3.js +0 -40
  335. package/dist/assets/ja-BYsakGFh.js +0 -16
  336. package/dist/assets/ko-BSaQz9aN.js +0 -16
  337. package/dist/assets/main-B62_pjL5.js +0 -2
  338. package/dist/assets/main-BEdcOI2P.js +0 -896
  339. package/dist/assets/miniChat-LT8Rqofr.js +0 -2
  340. package/dist/assets/mobile-CqarEynh.js +0 -2
  341. package/dist/assets/pl-OIviPdoj.js +0 -16
  342. package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
  343. package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
  344. package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
  345. package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
  346. package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
  347. package/dist/assets/tr-CQI-KPre.js +0 -7
  348. package/dist/assets/uk-CqW7R4DA.js +0 -16
  349. package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
  350. package/dist/assets/useRootScrollLock-Cpf1qoGp.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-CVZG_o_J.js +0 -16
  354. package/dist/assets/zh-TW-D5ZxzCZp.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
@@ -1,4 +1,4 @@
1
- import { buildDeferredRestartResponse } from './config-mutation-response.js';
1
+ import { buildAppliedResponse } from './config-mutation-response.js';
2
2
 
3
3
  export const registerConfigEntityRoutes = (app, dependencies) => {
4
4
  const {
@@ -6,10 +6,12 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
6
6
  resolveOptionalProjectDirectory,
7
7
  getAgentSources,
8
8
  getAgentConfig,
9
+ getAgentPermissions,
9
10
  createAgent,
10
11
  updateAgent,
11
12
  deleteAgent,
12
13
  getCommandSources,
14
+ getCommandConfig,
13
15
  createCommand,
14
16
  updateCommand,
15
17
  deleteCommand,
@@ -29,10 +31,11 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
29
31
  // Persist to disk immediately; OpenCode restart is deferred to an explicit
30
32
  // Apply & Restart so settings edits do not interrupt live sessions.
31
33
  const completeMcpMutation = async (res, action, name, applyChange) => {
32
- applyChange();
34
+ const result = applyChange();
33
35
  const past = action === 'delete' ? 'deleted' : `${action}d`;
34
- return res.json(buildDeferredRestartResponse(
35
- `MCP server "${name}" ${past}. Restart OpenCode to apply.`,
36
+ return res.json(buildAppliedResponse(
37
+ `MCP server "${name}" ${past}.`,
38
+ result,
36
39
  ));
37
40
  };
38
41
 
@@ -77,6 +80,20 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
77
80
  }
78
81
  });
79
82
 
83
+ app.get('/api/config/agents/:name/permissions', async (req, res) => {
84
+ try {
85
+ const agentName = req.params.name;
86
+ const { directory, error } = await resolveProjectDirectory(req);
87
+ if (!directory) {
88
+ return res.status(400).json({ error });
89
+ }
90
+ res.json(getAgentPermissions(agentName, directory));
91
+ } catch (error) {
92
+ console.error('Failed to get agent permissions:', error);
93
+ res.status(500).json({ error: 'Failed to get agent permissions' });
94
+ }
95
+ });
96
+
80
97
  app.post('/api/config/agents/:name', async (req, res) => {
81
98
  try {
82
99
  const agentName = req.params.name;
@@ -90,9 +107,10 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
90
107
  console.log('[Server] Config received:', JSON.stringify(config, null, 2));
91
108
  console.log('[Server] Scope:', scope, 'Working directory:', directory);
92
109
 
93
- createAgent(agentName, config, directory, scope);
94
- res.json(buildDeferredRestartResponse(
95
- `Agent ${agentName} created successfully. Restart OpenCode to apply.`,
110
+ const created = createAgent(agentName, config, directory, scope);
111
+ res.json(buildAppliedResponse(
112
+ `Agent ${agentName} created successfully.`,
113
+ created,
96
114
  ));
97
115
  } catch (error) {
98
116
  console.error('Failed to create agent:', error);
@@ -113,12 +131,13 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
113
131
  console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
114
132
  console.log('[Server] Working directory:', directory);
115
133
 
116
- updateAgent(agentName, updates, directory);
134
+ const updated = updateAgent(agentName, updates, directory);
117
135
 
118
136
  console.log(`[Server] Agent ${agentName} updated successfully`);
119
137
 
120
- res.json(buildDeferredRestartResponse(
121
- `Agent ${agentName} updated successfully. Restart OpenCode to apply.`,
138
+ res.json(buildAppliedResponse(
139
+ `Agent ${agentName} updated successfully.`,
140
+ updated,
122
141
  ));
123
142
  } catch (error) {
124
143
  console.error('[Server] Failed to update agent:', error);
@@ -137,8 +156,8 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
137
156
 
138
157
  const scope = req.body?.scope;
139
158
  deleteAgent(agentName, directory, scope);
140
- res.json(buildDeferredRestartResponse(
141
- `Agent ${agentName} deleted successfully. Restart OpenCode to apply.`,
159
+ res.json(buildAppliedResponse(
160
+ `Agent ${agentName} deleted successfully.`,
142
161
  ));
143
162
  } catch (error) {
144
163
  console.error('Failed to delete agent:', error);
@@ -188,9 +207,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
188
207
  }
189
208
  console.log(`[API:POST /api/config/mcp] Creating MCP server: ${name}`);
190
209
 
191
- await completeMcpMutation(res, 'create', name, () => {
192
- createMcpConfig(name, config, directory, scope);
193
- });
210
+ await completeMcpMutation(res, 'create', name, () => createMcpConfig(name, config, directory, scope));
194
211
  } catch (error) {
195
212
  console.error('[API:POST /api/config/mcp/:name] Failed:', error);
196
213
  res.status(500).json({ error: error.message || 'Failed to create MCP server' });
@@ -207,9 +224,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
207
224
  }
208
225
  console.log(`[API:PATCH /api/config/mcp] Updating MCP server: ${name}`);
209
226
 
210
- await completeMcpMutation(res, 'update', name, () => {
211
- updateMcpConfig(name, updates, directory);
212
- });
227
+ await completeMcpMutation(res, 'update', name, () => updateMcpConfig(name, updates, directory));
213
228
  } catch (error) {
214
229
  console.error('[API:PATCH /api/config/mcp/:name] Failed:', error);
215
230
  if (error?.message === `MCP server "${req.params.name}" not found`) {
@@ -228,9 +243,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
228
243
  }
229
244
  console.log(`[API:DELETE /api/config/mcp] Deleting MCP server: ${name}`);
230
245
 
231
- await completeMcpMutation(res, 'delete', name, () => {
232
- deleteMcpConfig(name, directory);
233
- });
246
+ await completeMcpMutation(res, 'delete', name, () => deleteMcpConfig(name, directory));
234
247
  } catch (error) {
235
248
  console.error('[API:DELETE /api/config/mcp/:name] Failed:', error);
236
249
  res.status(500).json({ error: error.message || 'Failed to delete MCP server' });
@@ -262,6 +275,20 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
262
275
  }
263
276
  });
264
277
 
278
+ app.get('/api/config/commands/:name/config', async (req, res) => {
279
+ try {
280
+ const commandName = req.params.name;
281
+ const { directory, error } = await resolveProjectDirectory(req);
282
+ if (!directory) {
283
+ return res.status(400).json({ error });
284
+ }
285
+ res.json(getCommandConfig(commandName, directory));
286
+ } catch (error) {
287
+ console.error('Failed to get command config:', error);
288
+ res.status(500).json({ error: 'Failed to get command configuration' });
289
+ }
290
+ });
291
+
265
292
  app.post('/api/config/commands/:name', async (req, res) => {
266
293
  try {
267
294
  const commandName = req.params.name;
@@ -275,9 +302,10 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
275
302
  console.log('[Server] Config received:', JSON.stringify(config, null, 2));
276
303
  console.log('[Server] Scope:', scope, 'Working directory:', directory);
277
304
 
278
- createCommand(commandName, config, directory, scope);
279
- res.json(buildDeferredRestartResponse(
280
- `Command ${commandName} created successfully. Restart OpenCode to apply.`,
305
+ const created = createCommand(commandName, config, directory, scope);
306
+ res.json(buildAppliedResponse(
307
+ `Command ${commandName} created successfully.`,
308
+ created,
281
309
  ));
282
310
  } catch (error) {
283
311
  console.error('Failed to create command:', error);
@@ -298,12 +326,13 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
298
326
  console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
299
327
  console.log('[Server] Working directory:', directory);
300
328
 
301
- updateCommand(commandName, updates, directory);
329
+ const updated = updateCommand(commandName, updates, directory);
302
330
 
303
331
  console.log(`[Server] Command ${commandName} updated successfully`);
304
332
 
305
- res.json(buildDeferredRestartResponse(
306
- `Command ${commandName} updated successfully. Restart OpenCode to apply.`,
333
+ res.json(buildAppliedResponse(
334
+ `Command ${commandName} updated successfully.`,
335
+ updated,
307
336
  ));
308
337
  } catch (error) {
309
338
  console.error('[Server] Failed to update command:', error);
@@ -321,8 +350,8 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
321
350
  }
322
351
 
323
352
  deleteCommand(commandName, directory);
324
- res.json(buildDeferredRestartResponse(
325
- `Command ${commandName} deleted successfully. Restart OpenCode to apply.`,
353
+ res.json(buildAppliedResponse(
354
+ `Command ${commandName} deleted successfully.`,
326
355
  ));
327
356
  } catch (error) {
328
357
  console.error('Failed to delete command:', error);
@@ -0,0 +1,290 @@
1
+ import { afterAll, beforeEach, describe, expect, it } from 'vitest';
2
+ import fs from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+
6
+ // The path constants are frozen when the modules load, so point
7
+ // XDG_CONFIG_HOME at a scratch directory BEFORE importing them. Nothing in this
8
+ // file may touch the real ~/.config/opencode.
9
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-entity-v2-'));
10
+ process.env.XDG_CONFIG_HOME = path.join(root, 'xdg');
11
+ delete process.env.OPENCODE_CONFIG;
12
+
13
+ const { parseMdFile } = await import('./shared.js');
14
+ const { getAgentConfig, getAgentPermissions, updateAgent, createAgent } = await import('./agents.js');
15
+ const { getCommandConfig, updateCommand } = await import('./commands.js');
16
+ const { getMcpConfig, listMcpConfigs, updateMcpConfig } = await import('./mcp.js');
17
+
18
+ let projectDir;
19
+
20
+ function write(relativePath, content) {
21
+ const filePath = path.join(projectDir, relativePath);
22
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
23
+ fs.writeFileSync(filePath, content, 'utf8');
24
+ return filePath;
25
+ }
26
+
27
+ function readJson(filePath) {
28
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
29
+ }
30
+
31
+ describe('entity modules speak OpenCode 2 shapes', () => {
32
+ beforeEach(() => {
33
+ projectDir = fs.mkdtempSync(path.join(root, 'project-'));
34
+ });
35
+
36
+ afterAll(() => {
37
+ fs.rmSync(root, { recursive: true, force: true });
38
+ delete process.env.XDG_CONFIG_HOME;
39
+ });
40
+
41
+ it('reads a v1 agent markdown file and answers the v2 shape', () => {
42
+ write('.opencode/agent/reviewer.md', [
43
+ '---',
44
+ 'description: Reviewer',
45
+ 'model: anthropic/claude-sonnet-4-5',
46
+ 'variant: high',
47
+ 'temperature: 0.4',
48
+ 'maxSteps: 9',
49
+ 'permission:',
50
+ ' bash:',
51
+ ' "git push *": ask',
52
+ ' edit: deny',
53
+ '---',
54
+ '',
55
+ 'Review for correctness.',
56
+ ].join('\n'));
57
+
58
+ const result = getAgentConfig('reviewer', projectDir);
59
+ expect(result.source).toBe('md');
60
+ expect(result.legacy).toBe(true);
61
+ expect(result.config).toEqual({
62
+ description: 'Reviewer',
63
+ model: 'anthropic/claude-sonnet-4-5#high',
64
+ steps: 9,
65
+ system: 'Review for correctness.',
66
+ request: { body: { temperature: 0.4 } },
67
+ permissions: [
68
+ { action: 'shell', resource: 'git push *', effect: 'ask' },
69
+ { action: 'edit', resource: '*', effect: 'deny' },
70
+ ],
71
+ });
72
+ });
73
+
74
+ it('rewrites a v1 agent file in place, at its own path, in v2 shape', () => {
75
+ const agentPath = write('.opencode/agent/reviewer.md', [
76
+ '---',
77
+ 'description: Reviewer',
78
+ 'temperature: 0.4',
79
+ 'permission:',
80
+ ' bash: ask',
81
+ '---',
82
+ '',
83
+ 'Old prompt.',
84
+ ].join('\n'));
85
+
86
+ const result = updateAgent('reviewer', { description: 'Careful reviewer' }, projectDir);
87
+
88
+ // Same file: OpenChamber never moves an entity between directories.
89
+ expect(result.path).toBe(agentPath);
90
+ expect(fs.existsSync(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'))).toBe(false);
91
+
92
+ const parsed = parseMdFile(agentPath);
93
+ expect(parsed.frontmatter).toEqual({
94
+ description: 'Careful reviewer',
95
+ request: { body: { temperature: 0.4 } },
96
+ permissions: [{ action: 'shell', resource: '*', effect: 'ask' }],
97
+ });
98
+ expect(parsed.body).toBe('Old prompt.');
99
+ });
100
+
101
+ it('writes the permission rule array when the client sends a v1 map', () => {
102
+ write('.opencode/agents/reviewer.md', '---\ndescription: Reviewer\n---\n\nPrompt.\n');
103
+
104
+ updateAgent('reviewer', { permission: { task: 'deny', write: 'allow' } }, projectDir);
105
+
106
+ const parsed = parseMdFile(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'));
107
+ expect(parsed.frontmatter.permissions).toEqual([
108
+ { action: 'subagent', resource: '*', effect: 'deny' },
109
+ { action: 'edit', resource: '*', effect: 'allow' },
110
+ ]);
111
+ expect(parsed.frontmatter.permission).toBeUndefined();
112
+ });
113
+
114
+ it('creates a new agent in the v2 agents directory', () => {
115
+ createAgent('fresh', { description: 'Fresh', system: 'Do the thing.' }, projectDir, 'project');
116
+
117
+ const created = path.join(projectDir, '.opencode', 'agents', 'fresh.md');
118
+ expect(fs.existsSync(created)).toBe(true);
119
+ expect(parseMdFile(created)).toEqual({
120
+ frontmatter: { description: 'Fresh' },
121
+ body: 'Do the thing.',
122
+ });
123
+ });
124
+
125
+ it('moves a v1 JSON agent entry into the agents section of the same file', () => {
126
+ const configPath = write('opencode.json', JSON.stringify({
127
+ agent: {
128
+ reviewer: { prompt: 'Old', permission: { bash: 'ask' } },
129
+ other: { prompt: 'Untouched' },
130
+ },
131
+ }, null, 2));
132
+
133
+ updateAgent('reviewer', { system: 'New' }, projectDir);
134
+
135
+ const written = readJson(configPath);
136
+ expect(written.agent).toEqual({ other: { prompt: 'Untouched' } });
137
+ expect(written.agents.reviewer).toEqual({
138
+ system: 'New',
139
+ permissions: [{ action: 'shell', resource: '*', effect: 'ask' }],
140
+ });
141
+ });
142
+
143
+ it('clears v1 fields at the v2 location they moved to', () => {
144
+ const configPath = write('opencode.json', JSON.stringify({
145
+ agent: { build: { variant: 'fast', temperature: 0.3, top_p: 0.8, mode: 'subagent' } },
146
+ }, null, 2));
147
+
148
+ // The client still names the v1 fields, but on a v2 entity `variant` is the
149
+ // `#` suffix on `model` and `temperature`/`top_p` live in `request.body`.
150
+ updateAgent('build', { variant: null, temperature: null, top_p: null }, projectDir);
151
+
152
+ const written = readJson(configPath);
153
+ expect(written.agent).toBeUndefined();
154
+ expect(written.agents.build).toEqual({ mode: 'subagent' });
155
+ });
156
+
157
+ it('drops only the variant when a model is set', () => {
158
+ write('.opencode/agents/reviewer.md', '---\nmodel: anthropic/claude-sonnet-4-5#high\n---\n\nPrompt.\n');
159
+
160
+ updateAgent('reviewer', { variant: null }, projectDir);
161
+
162
+ const parsed = parseMdFile(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'));
163
+ expect(parsed.frontmatter.model).toBe('anthropic/claude-sonnet-4-5');
164
+ });
165
+
166
+ it('reports global and agent rules in evaluation order', () => {
167
+ write('opencode.json', JSON.stringify({
168
+ permission: { edit: 'allow' },
169
+ tools: { websearch: false },
170
+ }, null, 2));
171
+ write('.opencode/agents/reviewer.md', [
172
+ '---',
173
+ 'permissions:',
174
+ ' - action: edit',
175
+ ' resource: "*"',
176
+ ' effect: deny',
177
+ '---',
178
+ '',
179
+ 'Prompt.',
180
+ ].join('\n'));
181
+
182
+ const result = getAgentPermissions('reviewer', projectDir);
183
+ expect(result.agent).toEqual([{ action: 'edit', resource: '*', effect: 'deny' }]);
184
+ expect(result.effective).toEqual([
185
+ { action: 'websearch', resource: '*', effect: 'deny', source: 'global' },
186
+ { action: 'edit', resource: '*', effect: 'allow', source: 'global' },
187
+ { action: 'edit', resource: '*', effect: 'deny', source: 'agent' },
188
+ ]);
189
+ });
190
+
191
+ it('reads a v1 command file and rewrites it in place as subagent', () => {
192
+ const commandPath = write('.opencode/command/review.md', [
193
+ '---',
194
+ 'description: Review',
195
+ 'model: anthropic/claude-sonnet-4-5',
196
+ 'variant: high',
197
+ 'subtask: true',
198
+ '---',
199
+ '',
200
+ 'Review the current changes.',
201
+ ].join('\n'));
202
+
203
+ expect(getCommandConfig('review', projectDir).config).toEqual({
204
+ description: 'Review',
205
+ model: 'anthropic/claude-sonnet-4-5#high',
206
+ subagent: true,
207
+ template: 'Review the current changes.',
208
+ });
209
+
210
+ const result = updateCommand('review', { description: 'Review changes' }, projectDir);
211
+ expect(result.path).toBe(commandPath);
212
+ expect(parseMdFile(commandPath).frontmatter).toEqual({
213
+ description: 'Review changes',
214
+ model: 'anthropic/claude-sonnet-4-5#high',
215
+ subagent: true,
216
+ });
217
+ });
218
+
219
+ it('rewrites a v1 theme color as the hex OpenCode migrates it to', () => {
220
+ const agentPath = write('.opencode/agent/tinted.md', [
221
+ '---',
222
+ 'description: Tinted',
223
+ 'color: primary',
224
+ '---',
225
+ '',
226
+ 'Prompt.',
227
+ ].join('\n'));
228
+
229
+ updateAgent('tinted', { description: 'Still tinted' }, projectDir);
230
+
231
+ // `primary` is not a v2 color; the native decoder would skip the whole file.
232
+ expect(parseMdFile(agentPath).frontmatter).toEqual({ description: 'Still tinted', color: '#aaaaaa' });
233
+ });
234
+
235
+ it('answers the project v1 override when the user file already holds the v2 spelling', () => {
236
+ const userConfigPath = path.join(process.env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
237
+ fs.mkdirSync(path.dirname(userConfigPath), { recursive: true });
238
+ fs.writeFileSync(userConfigPath, JSON.stringify({
239
+ mcp: { servers: { docs: { type: 'remote', url: 'https://global.example.com/mcp' } } },
240
+ }, null, 2), 'utf8');
241
+ const projectConfigPath = write('.opencode/opencode.json', JSON.stringify({
242
+ mcp: { docs: { type: 'remote', url: 'https://project.example.com/mcp', timeout: 5000 } },
243
+ }, null, 2));
244
+
245
+ try {
246
+ expect(getMcpConfig('docs', projectDir)).toEqual(expect.objectContaining({
247
+ url: 'https://project.example.com/mcp',
248
+ scope: 'project',
249
+ legacy: true,
250
+ }));
251
+ expect(listMcpConfigs(projectDir).find((entry) => entry.name === 'docs').url).toBe('https://project.example.com/mcp');
252
+
253
+ updateMcpConfig('docs', { timeout: { catalog: 9000, execution: 9000 } }, projectDir);
254
+
255
+ expect(readJson(projectConfigPath).mcp).toEqual({
256
+ servers: { docs: { type: 'remote', url: 'https://project.example.com/mcp', timeout: { catalog: 9000, execution: 9000 } } },
257
+ });
258
+ expect(readJson(userConfigPath).mcp.servers.docs.url).toBe('https://global.example.com/mcp');
259
+ } finally {
260
+ fs.rmSync(userConfigPath, { force: true });
261
+ }
262
+ });
263
+
264
+ it('reads a v1 mcp entry and rewrites it under mcp.servers in the same file', () => {
265
+ const configPath = write('opencode.json', JSON.stringify({
266
+ mcp: {
267
+ playwright: { type: 'local', command: ['npx', '@playwright/mcp'], enabled: true, timeout: 30000 },
268
+ },
269
+ }, null, 2));
270
+
271
+ expect(getMcpConfig('playwright', projectDir)).toEqual(expect.objectContaining({
272
+ name: 'playwright',
273
+ type: 'local',
274
+ disabled: false,
275
+ timeout: { catalog: 30000, execution: 30000 },
276
+ legacy: true,
277
+ }));
278
+
279
+ updateMcpConfig('playwright', { disabled: true }, projectDir);
280
+
281
+ const written = readJson(configPath);
282
+ expect(written.mcp.playwright).toBeUndefined();
283
+ expect(written.mcp.servers.playwright).toEqual({
284
+ type: 'local',
285
+ command: ['npx', '@playwright/mcp'],
286
+ disabled: true,
287
+ timeout: { catalog: 30000, execution: 30000 },
288
+ });
289
+ });
290
+ });
@@ -1,18 +1,28 @@
1
1
  /**
2
2
  * Shared response shapes for OpenCode config mutations.
3
3
  *
4
- * Settings writes persist to disk immediately but defer the OpenCode restart
5
- * so the UI can accumulate pending changes and apply them once via
6
- * POST /api/config/reload ("Apply & Restart OpenCode").
4
+ * OpenCode 2 watches every config source it reads — agents, commands, skills,
5
+ * MCP servers, providers, instructions and the plugin list — and rebuilds them
6
+ * when a file changes. A mutation that lands on disk is therefore already live
7
+ * by the time the route answers, so config mutations report plain success.
8
+ *
9
+ * Only changes OpenChamber cannot hand to a running process still ask for a
10
+ * restart: the OpenCode binary, its port, and switching between managed and
11
+ * external OpenCode. Those keep `buildExternalManualRestartResponse` and the
12
+ * manual `POST /api/config/reload`.
7
13
  */
8
14
 
9
- export function buildDeferredRestartResponse(message) {
15
+ /**
16
+ * `details` carries the file the mutation landed in (`{ path, scope, source }`).
17
+ * OpenChamber never moves an entity between files, so the caller can show the
18
+ * user exactly which config file changed — including a v1 file that was
19
+ * rewritten in place in v2 shape.
20
+ */
21
+ export function buildAppliedResponse(message, details) {
10
22
  return {
11
23
  success: true,
12
- requiresReload: false,
13
- requiresRestart: true,
14
- restartDeferred: true,
15
24
  message,
25
+ ...(details && typeof details === 'object' ? details : {}),
16
26
  };
17
27
  }
18
28
 
@@ -1,18 +1,15 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
2
 
3
3
  import {
4
- buildDeferredRestartResponse,
4
+ buildAppliedResponse,
5
5
  buildExternalManualRestartResponse,
6
6
  } from './config-mutation-response.js';
7
7
 
8
8
  describe('config mutation response helpers', () => {
9
- test('buildDeferredRestartResponse marks restart as deferred', () => {
10
- expect(buildDeferredRestartResponse('Saved. Restart OpenCode to apply.')).toEqual({
9
+ test('buildAppliedResponse reports plain success with no restart flags', () => {
10
+ expect(buildAppliedResponse('Agent saved.')).toEqual({
11
11
  success: true,
12
- requiresReload: false,
13
- requiresRestart: true,
14
- restartDeferred: true,
15
- message: 'Saved. Restart OpenCode to apply.',
12
+ message: 'Agent saved.',
16
13
  });
17
14
  });
18
15
 
@@ -38,7 +38,7 @@ describe('OpenCode global config paths', () => {
38
38
  expect(AGENT_DIR).toBe(path.join(configDir, 'agents'));
39
39
  expect(COMMAND_DIR).toBe(path.join(configDir, 'commands'));
40
40
  expect(SKILL_DIR).toBe(path.join(configDir, 'skills'));
41
- expect(CONFIG_FILE).toBe(path.join(configDir, 'config.json'));
41
+ expect(CONFIG_FILE).toBe(path.join(configDir, 'opencode.json'));
42
42
 
43
43
  fs.rmSync(xdgConfigHome, { recursive: true, force: true });
44
44
  });
@@ -70,7 +70,7 @@ describe('OpenCode global config paths', () => {
70
70
  expect(fs.existsSync(path.join(configDir, 'commands', 'global-command.md'))).toBe(true);
71
71
  expect(fs.existsSync(path.join(configDir, 'skills', 'global-skill', 'SKILL.md'))).toBe(true);
72
72
  expect(fs.existsSync(path.join(configDir, 'snippet', 'global-snippet.md'))).toBe(true);
73
- expect(JSON.parse(fs.readFileSync(path.join(configDir, 'config.json'), 'utf8')).plugin).toEqual(['global-plugin']);
73
+ expect(JSON.parse(fs.readFileSync(path.join(configDir, 'opencode.json'), 'utf8')).plugins).toEqual(['global-plugin']);
74
74
 
75
75
  agents.createAgent('project-agent', { description: 'Project', prompt: 'Project prompt' }, projectDir, 'project');
76
76
  commands.createCommand('project-command', { description: 'Project', template: 'Project template' }, projectDir, 'project');