@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
@@ -20,7 +20,6 @@ const createRuntime = async (overrides = {}) => {
20
20
  const dataDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openchamber-agent-tool-'));
21
21
  temporaryDirectories.push(dataDir);
22
22
  const executeAction = vi.fn(async () => ({ projects: [] }));
23
- const env = {};
24
23
  const runtime = createAgentToolRuntime({
25
24
  crypto,
26
25
  fsPromises: fs,
@@ -28,10 +27,32 @@ const createRuntime = async (overrides = {}) => {
28
27
  dataDir,
29
28
  getActivePort: () => 3901,
30
29
  executeAction,
31
- env,
32
30
  ...overrides,
33
31
  });
34
- return { runtime, dataDir, executeAction, env };
32
+ return { runtime, dataDir, executeAction };
33
+ };
34
+
35
+ const pluginDirectoryFor = (dataDir) => path.join(dataDir, 'agent-tool', 'openchamber-agent-tool');
36
+
37
+ /** What a managed OpenCode child start does: write the plugin, mint a token. */
38
+ const prepareManagedEnv = async (runtime, options) => {
39
+ const pluginDirectory = await runtime.materializePlugin(options);
40
+ return { pluginDirectory, ...runtime.createChildEnv() };
41
+ };
42
+
43
+ /** Loads the generated plugin and returns the tools it registers, by name. */
44
+ const loadTools = async (dataDir, tag) => {
45
+ const entrypoint = path.join(pluginDirectoryFor(dataDir), 'index.js');
46
+ const pluginModule = await import(`${pathToFileURL(entrypoint).href}?${tag}=${Date.now()}`);
47
+ const registered = {};
48
+ await pluginModule.default.setup({
49
+ tool: {
50
+ transform: (edit) => {
51
+ edit({ add: (tool) => { registered[tool.name] = tool; } });
52
+ },
53
+ },
54
+ });
55
+ return registered;
35
56
  };
36
57
 
37
58
  describe('agent tool action allowlist', () => {
@@ -53,6 +74,7 @@ describe('agent tool action allowlist', () => {
53
74
  'schedule.run',
54
75
  'schedule.delete',
55
76
  'schedule.toggle',
77
+ 'file.open',
56
78
  ])('delegates %s to the shared control service', async (action) => {
57
79
  const { runtime, executeAction } = await createRuntime();
58
80
  const input = { action, projectId: 'project-1' };
@@ -75,21 +97,16 @@ describe('agent tool action allowlist', () => {
75
97
  });
76
98
 
77
99
  describe('managed agent tool runtime', () => {
78
- it('materializes the plugin and preserves configured plugin entries', async () => {
79
- const { runtime, dataDir, env } = await createRuntime();
80
- env.OPENCODE_CONFIG_CONTENT = '{ // existing\n "plugin": ["file:///existing.js", ["example-plugin", {"flag": true}]], "model": "test/model" }';
81
-
82
- const preparedEnv = await runtime.prepareManagedOpenCodeEnv();
83
- const config = JSON.parse(preparedEnv.OPENCODE_CONFIG_CONTENT);
84
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
85
- const source = await fs.readFile(pluginPath, 'utf8');
86
-
87
- expect(config.model).toBe('test/model');
88
- expect(config.plugin).toEqual([
89
- 'file:///existing.js',
90
- ['example-plugin', { flag: true }],
91
- expect.stringContaining('/agent-tool/openchamber-plugin.js'),
92
- ]);
100
+ it('materializes the plugin and mints a per-child callback token', async () => {
101
+ const { runtime, dataDir } = await createRuntime();
102
+
103
+ const preparedEnv = await prepareManagedEnv(runtime);
104
+ const source = await fs.readFile(path.join(pluginDirectoryFor(dataDir), 'index.js'), 'utf8');
105
+ const manifest = JSON.parse(await fs.readFile(path.join(pluginDirectoryFor(dataDir), 'package.json'), 'utf8'));
106
+
107
+ // The managed config layer names this directory; the runtime only writes it.
108
+ expect(preparedEnv.pluginDirectory).toBe(pluginDirectoryFor(dataDir));
109
+ expect(manifest.exports).toEqual({ '.': './index.js' });
93
110
  expect(preparedEnv.OPENCHAMBER_AGENT_TOOL_URL).toBe('http://127.0.0.1:3901/api/openchamber/agent-tool');
94
111
  expect(preparedEnv.OPENCHAMBER_AGENT_TOOL_TOKEN).toMatch(/^[A-Za-z0-9_-]+$/);
95
112
  expect(source).toContain('openchamber: {');
@@ -97,40 +114,40 @@ describe('managed agent tool runtime', () => {
97
114
  expect(source).toContain(JSON.stringify({ const: action, description }));
98
115
  }
99
116
  expect(source).not.toContain('"schedule.status"');
100
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?schema=${Date.now()}`);
101
- const hooks = await pluginModule.OpenChamberPlugin();
102
- expect(hooks.tool.openchamber.description).toContain('Session dispatches return immediately by default');
103
- expect(hooks.tool.openchamber.description).toContain('Set wait only when the user asks or the next step requires the completed result');
104
- expect(hooks.tool.openchamber.args.action.oneOf).toContainEqual({
117
+ const tool = await loadTools(dataDir, 'schema');
118
+ expect(tool.openchamber.description).toContain('Session dispatches return immediately by default');
119
+ expect(tool.openchamber.description).toContain('Set wait only when the user asks or the next step requires the completed result');
120
+ expect(tool.openchamber.input.properties.action.oneOf).toContainEqual({
105
121
  const: 'session.messages',
106
122
  description: 'Read text-only messages and current sessionStatus for sessionId; directory and limit 10 are defaults',
107
123
  });
108
- expect(hooks.tool.openchamber.args.parameters.properties.wait.description).toBe(
124
+ expect(tool.openchamber.input.properties.parameters.properties.wait.description).toBe(
109
125
  'Wait for current session activity to become idle. Omit by default; use only when the user asks or the next step requires the completed result',
110
126
  );
111
- expect(hooks.tool.openchamber.args.parameters.properties.sessionId).toEqual({ type: 'string' });
127
+ expect(tool.openchamber.input.properties.parameters.properties.sessionId).toEqual({ type: 'string' });
112
128
  expect(source).not.toContain('title: "OpenChamber"');
113
- expect(source).not.toContain('@opencode-ai/plugin');
129
+ // Nothing resolves from the generated directory, so the file must not import.
130
+ expect(source).not.toMatch(/\bimport\b/);
114
131
  expect(source).not.toContain(preparedEnv.OPENCHAMBER_AGENT_TOOL_TOKEN);
115
132
  });
116
133
 
117
134
  it('emits both tools, each carrying only its own actions and inputs', async () => {
118
135
  const { runtime, dataDir } = await createRuntime();
119
- await runtime.prepareManagedOpenCodeEnv();
120
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
121
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?both=${Date.now()}`);
122
- const { tool } = await pluginModule.OpenChamberPlugin();
136
+ await prepareManagedEnv(runtime);
137
+ const tool = await loadTools(dataDir, 'both');
123
138
 
124
- const controlActions = tool.openchamber.args.action.oneOf.map((entry) => entry.const);
125
- const webActions = tool.openchamber_web.args.action.oneOf.map((entry) => entry.const);
139
+ const controlActions = tool.openchamber.input.properties.action.oneOf.map((entry) => entry.const);
140
+ const webActions = tool.openchamber_web.input.properties.action.oneOf.map((entry) => entry.const);
126
141
  expect(webActions).toContain('browser.open');
127
142
  expect(controlActions).not.toContain('browser.open');
128
143
  expect(webActions).not.toContain('session.create');
129
144
 
130
145
  // Turning one tool off has to remove its inputs too, not just its actions.
131
- expect(Object.keys(tool.openchamber_web.args.parameters.properties)).toContain('url');
132
- expect(Object.keys(tool.openchamber.args.parameters.properties)).not.toContain('url');
133
- expect(Object.keys(tool.openchamber.args.parameters.properties)).toContain('sessionId');
146
+ expect(Object.keys(tool.openchamber_web.input.properties.parameters.properties)).toContain('url');
147
+ expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).not.toContain('url');
148
+ expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).toContain('sessionId');
149
+ expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).toContain('path');
150
+ expect(Object.keys(tool.openchamber_web.input.properties.parameters.properties)).not.toContain('path');
134
151
  });
135
152
 
136
153
  it('keeps the action schema to one validator keyword', async () => {
@@ -139,23 +156,19 @@ describe('managed agent tool runtime', () => {
139
156
  // instead of an error. `oneOf` is the keyword that stayed. Its branches
140
157
  // carry the per-action descriptions the model reads.
141
158
  const { runtime, dataDir } = await createRuntime();
142
- await runtime.prepareManagedOpenCodeEnv();
143
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
144
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?validator=${Date.now()}`);
145
- const { tool } = await pluginModule.OpenChamberPlugin();
159
+ await prepareManagedEnv(runtime);
160
+ const tool = await loadTools(dataDir, 'validator');
146
161
 
147
162
  for (const entry of Object.values(tool)) {
148
- expect(entry.args.action.oneOf).toBeInstanceOf(Array);
149
- expect(entry.args.action).not.toHaveProperty('enum');
163
+ expect(entry.input.properties.action.oneOf).toBeInstanceOf(Array);
164
+ expect(entry.input.properties.action).not.toHaveProperty('enum');
150
165
  }
151
166
  });
152
167
 
153
168
  it('accepts inputs passed beside the action, not only inside parameters', async () => {
154
169
  const { runtime, dataDir } = await createRuntime();
155
- const prepared = await runtime.prepareManagedOpenCodeEnv();
156
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
157
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?flat=${Date.now()}`);
158
- const { tool } = await pluginModule.OpenChamberPlugin();
170
+ const prepared = await prepareManagedEnv(runtime);
171
+ const tool = await loadTools(dataDir, 'flat');
159
172
 
160
173
  const sent = [];
161
174
  const originalFetch = globalThis.fetch;
@@ -167,7 +180,7 @@ describe('managed agent tool runtime', () => {
167
180
  sent.push(JSON.parse(init.body));
168
181
  return new Response(JSON.stringify({ schemaVersion: 1, ok: true, action: 'browser.open', data: {} }));
169
182
  };
170
- const context = { directory: '/work/project', abort: new AbortController().signal, metadata: () => {} };
183
+ const context = { sessionID: 'ses_1', agent: 'build', messageID: 'msg_1', id: 'call_1', progress: async () => {} };
171
184
 
172
185
  try {
173
186
  // The shape a model actually produced: url and viewport next to action.
@@ -194,49 +207,44 @@ describe('managed agent tool runtime', () => {
194
207
  expect(sent[0].input).toEqual({ action: 'browser.open', url: 'https://example.test', viewport: 'mobile' });
195
208
  expect(sent[1].input.url).toBe('https://example.test/nested');
196
209
  expect(sent[2].input).toEqual({ action: 'session.messages', sessionId: 'ses_1', limit: 3 });
210
+ // v2 tools get no directory; the session id is what OpenChamber resolves from.
211
+ expect(sent[2].sessionID).toBe('ses_1');
212
+ expect(sent[2].contextDirectory).toBeUndefined();
197
213
  });
198
214
 
199
215
  it('omits a tool the user turned off', async () => {
200
216
  const { runtime, dataDir } = await createRuntime();
201
- await runtime.prepareManagedOpenCodeEnv({ includeControl: false, includeWeb: true, includeMemory: false });
202
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
203
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?web=${Date.now()}`);
204
- const { tool } = await pluginModule.OpenChamberPlugin();
217
+ await prepareManagedEnv(runtime, { includeControl: false, includeWeb: true, includeMemory: false });
218
+ const tool = await loadTools(dataDir, 'web');
205
219
 
206
220
  expect(Object.keys(tool)).toEqual(['openchamber_web']);
207
221
  });
208
222
 
209
223
  it('exposes memory as its own tool carrying only its own inputs', async () => {
210
224
  const { runtime, dataDir } = await createRuntime();
211
- await runtime.prepareManagedOpenCodeEnv({ includeControl: true, includeWeb: false, includeMemory: true });
212
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
213
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?memory=${Date.now()}`);
214
- const { tool } = await pluginModule.OpenChamberPlugin();
225
+ await prepareManagedEnv(runtime, { includeControl: true, includeWeb: false, includeMemory: true });
226
+ const tool = await loadTools(dataDir, 'memory');
215
227
 
216
228
  expect(Object.keys(tool)).toEqual(['openchamber', 'openchamber_memory']);
217
- expect(Object.keys(tool.openchamber_memory.args.parameters.properties).sort())
229
+ expect(Object.keys(tool.openchamber_memory.input.properties.parameters.properties).sort())
218
230
  .toEqual(['body', 'memoryId', 'scope', 'title', 'type']);
219
231
  // Memory inputs must not leak into the control tool's schema, which the
220
232
  // model pays for on every unrelated call.
221
- expect(Object.keys(tool.openchamber.args.parameters.properties)).not.toContain('memoryId');
233
+ expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).not.toContain('memoryId');
222
234
  });
223
235
 
224
236
  it('omits memory entirely when the user turns it off', async () => {
225
237
  const { runtime, dataDir } = await createRuntime();
226
- await runtime.prepareManagedOpenCodeEnv({ includeControl: true, includeWeb: false, includeMemory: false });
227
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
228
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?nomemory=${Date.now()}`);
229
- const { tool } = await pluginModule.OpenChamberPlugin();
238
+ await prepareManagedEnv(runtime, { includeControl: true, includeWeb: false, includeMemory: false });
239
+ const tool = await loadTools(dataDir, 'nomemory');
230
240
 
231
241
  expect(Object.keys(tool)).toEqual(['openchamber']);
232
242
  });
233
243
 
234
244
  it('injects the plugin when memory is the only tool left on', async () => {
235
245
  const { runtime, dataDir } = await createRuntime();
236
- await runtime.prepareManagedOpenCodeEnv({ includeControl: false, includeWeb: false, includeMemory: true });
237
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
238
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?onlymemory=${Date.now()}`);
239
- const { tool } = await pluginModule.OpenChamberPlugin();
246
+ await prepareManagedEnv(runtime, { includeControl: false, includeWeb: false, includeMemory: true });
247
+ const tool = await loadTools(dataDir, 'onlymemory');
240
248
 
241
249
  expect(Object.keys(tool)).toEqual(['openchamber_memory']);
242
250
  });
@@ -245,7 +253,7 @@ describe('managed agent tool runtime', () => {
245
253
  const { runtime } = await createRuntime();
246
254
  let failed = false;
247
255
  try {
248
- await runtime.prepareManagedOpenCodeEnv({ includeControl: false, includeWeb: false, includeMemory: false });
256
+ await prepareManagedEnv(runtime, { includeControl: false, includeWeb: false, includeMemory: false });
249
257
  } catch {
250
258
  failed = true;
251
259
  }
@@ -261,6 +269,7 @@ describe('managed agent tool runtime', () => {
261
269
  const result = await runtime.execute({
262
270
  input: { action: 'read', title: 'Uses bun' },
263
271
  contextDirectory: '/work/project',
272
+ contextSessionId: 'ses_1',
264
273
  tool: 'openchamber_memory',
265
274
  });
266
275
 
@@ -270,7 +279,7 @@ describe('managed agent tool runtime', () => {
270
279
  'memory.read',
271
280
  { action: 'memory.read', title: 'Uses bun' },
272
281
  '/work/project',
273
- {},
282
+ { contextSessionId: 'ses_1' },
274
283
  );
275
284
  });
276
285
 
@@ -317,6 +326,35 @@ describe('managed agent tool runtime', () => {
317
326
  expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, '/work/project', {});
318
327
  });
319
328
 
329
+ it('resolves the directory from the session id the plugin sends', async () => {
330
+ const executeAction = vi.fn(async () => ({}));
331
+ const resolveSessionDirectory = vi.fn(async () => '/work/other-worktree');
332
+ const { runtime } = await createRuntime({ executeAction, resolveSessionDirectory });
333
+
334
+ await runtime.execute({ input: { action: 'session.messages', sessionId: 'ses_1' }, sessionID: 'ses_1' });
335
+
336
+ expect(resolveSessionDirectory).toHaveBeenCalledWith('ses_1');
337
+ // The calling session also scopes the action (browser pages are per session).
338
+ expect(executeAction).toHaveBeenCalledWith(
339
+ 'session.messages',
340
+ { action: 'session.messages', sessionId: 'ses_1' },
341
+ '/work/other-worktree',
342
+ { contextSessionId: 'ses_1' },
343
+ );
344
+ });
345
+
346
+ it('runs without a directory when the session cannot be resolved', async () => {
347
+ const executeAction = vi.fn(async () => ({}));
348
+ const { runtime } = await createRuntime({
349
+ executeAction,
350
+ resolveSessionDirectory: vi.fn(async () => { throw new Error('unavailable'); }),
351
+ });
352
+
353
+ await runtime.execute({ input: { action: 'projects.list' }, sessionID: 'ses_1' });
354
+
355
+ expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, undefined, { contextSessionId: 'ses_1' });
356
+ });
357
+
320
358
  it('keeps service failures as structured tool results', async () => {
321
359
  const error = Object.assign(new Error('Task not found'), { statusCode: 404 });
322
360
  const { runtime } = await createRuntime({ executeAction: vi.fn(async () => { throw error; }) });
@@ -352,9 +390,45 @@ describe('managed agent tool runtime', () => {
352
390
  expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, undefined, { signal: controller.signal });
353
391
  });
354
392
 
393
+ it('aborts the actions a session still has running when its turn is cancelled', async () => {
394
+ // Safety net beside the plugin's forwarded abort signal: the server also
395
+ // ends the action itself when the event stream reports the cancel.
396
+ const executeAction = vi.fn(async (_action, _input, _directory, options) => {
397
+ await new Promise((resolve, reject) => {
398
+ options.signal.addEventListener('abort', () => reject(Object.assign(new Error('OpenChamber action was cancelled'), { statusCode: 499 })), { once: true });
399
+ });
400
+ });
401
+ const { runtime } = await createRuntime({ executeAction });
402
+ const env = await prepareManagedEnv(runtime);
403
+ const app = express();
404
+ runtime.registerRoutes(app, express);
405
+
406
+ // supertest only sends when awaited; `.then` starts both requests now.
407
+ const pending = request(app)
408
+ .post('/api/openchamber/agent-tool')
409
+ .set('authorization', `Bearer ${env.OPENCHAMBER_AGENT_TOOL_TOKEN}`)
410
+ .send({ input: { action: 'projects.list' }, sessionID: 'ses_cancel' })
411
+ .then((response) => response);
412
+ const other = request(app)
413
+ .post('/api/openchamber/agent-tool')
414
+ .set('authorization', `Bearer ${env.OPENCHAMBER_AGENT_TOOL_TOKEN}`)
415
+ .send({ input: { action: 'projects.list' }, sessionID: 'ses_other' })
416
+ .then((response) => response);
417
+ // Let both requests reach the service before cancelling one session.
418
+ await vi.waitFor(() => expect(executeAction).toHaveBeenCalledTimes(2));
419
+
420
+ expect(runtime.abortSession('ses_cancel')).toBe(1);
421
+ const response = await pending;
422
+ expect(response.body).toEqual(expect.objectContaining({ ok: false, error: expect.objectContaining({ message: 'OpenChamber action was cancelled' }) }));
423
+
424
+ // The other session's action is untouched until it is told otherwise.
425
+ expect(runtime.abortSession('ses_other')).toBe(1);
426
+ await other;
427
+ });
428
+
355
429
  it('requires the per-child token on the loopback route', async () => {
356
430
  const { runtime } = await createRuntime();
357
- const env = await runtime.prepareManagedOpenCodeEnv();
431
+ const env = await prepareManagedEnv(runtime);
358
432
  const app = express();
359
433
  runtime.registerRoutes(app, express);
360
434
 
@@ -380,7 +454,7 @@ describe('managed agent tool runtime', () => {
380
454
  ['fd7a:115c::3', 'http://[fd7a:115c::3]:3901/api/openchamber/agent-tool'],
381
455
  ])('points the callback at where a listener bound to %s answers', async (boundAddress, expectedUrl) => {
382
456
  const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
383
- const env = await runtime.prepareManagedOpenCodeEnv();
457
+ const env = await prepareManagedEnv(runtime);
384
458
  expect(env.OPENCHAMBER_AGENT_TOOL_URL).toBe(expectedUrl);
385
459
  });
386
460
 
@@ -395,7 +469,7 @@ describe('managed agent tool runtime', () => {
395
469
  [null, '192.168.1.20', 401],
396
470
  ])('bound to %s, answers a token-bearing caller from %s with %i', async (boundAddress, remoteAddress, status) => {
397
471
  const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
398
- const env = await runtime.prepareManagedOpenCodeEnv();
472
+ const env = await prepareManagedEnv(runtime);
399
473
  const app = express();
400
474
  app.use((req, _res, next) => {
401
475
  Object.defineProperty(req.socket, 'remoteAddress', { value: remoteAddress, configurable: true });
@@ -417,7 +491,7 @@ describe('managed agent tool runtime', () => {
417
491
  ['not a url', 'localhost', 'localhost'],
418
492
  ])('keeps the callback %s away from an environment proxy', async (callbackUrl, existing, expected) => {
419
493
  const { runtime, dataDir } = await createRuntime();
420
- await runtime.prepareManagedOpenCodeEnv();
494
+ await prepareManagedEnv(runtime);
421
495
  const keys = ['OPENCHAMBER_AGENT_TOOL_URL', 'NO_PROXY', 'no_proxy'];
422
496
  const previous = keys.map((key) => process.env[key]);
423
497
  try {
@@ -426,9 +500,7 @@ describe('managed agent tool runtime', () => {
426
500
  if (existing === undefined) delete process.env[key];
427
501
  else process.env[key] = existing;
428
502
  }
429
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
430
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
431
- await pluginModule.OpenChamberPlugin();
503
+ await loadTools(dataDir, 'proxy');
432
504
 
433
505
  expect(process.env.NO_PROXY).toBe(expected);
434
506
  expect(process.env.no_proxy).toBe(expected);
@@ -453,32 +525,28 @@ describe('managed agent tool runtime', () => {
453
525
  const previousUrl = process.env.OPENCHAMBER_AGENT_TOOL_URL;
454
526
  const previousToken = process.env.OPENCHAMBER_AGENT_TOOL_TOKEN;
455
527
  try {
456
- const env = await runtime.prepareManagedOpenCodeEnv();
528
+ const env = await prepareManagedEnv(runtime);
457
529
  process.env.OPENCHAMBER_AGENT_TOOL_URL = env.OPENCHAMBER_AGENT_TOOL_URL;
458
530
  process.env.OPENCHAMBER_AGENT_TOOL_TOKEN = env.OPENCHAMBER_AGENT_TOOL_TOKEN;
459
- const pluginPath = path.join(dataDir, 'agent-tool', 'openchamber-plugin.js');
460
- const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
461
- const hooks = await pluginModule.OpenChamberPlugin();
462
- const metadata = vi.fn();
531
+ const tool = await loadTools(dataDir, 'callback');
532
+ const progress = vi.fn(async () => {});
463
533
 
464
- const result = await hooks.tool.openchamber.execute(
534
+ const result = await tool.openchamber.execute(
465
535
  { action: 'projects.list', parameters: {} },
466
- { directory: '/work/project', abort: new AbortController().signal, metadata },
536
+ { sessionID: 'ses_1', agent: 'build', messageID: 'msg_1', id: 'call_1', progress },
467
537
  );
468
538
 
469
- expect(JSON.parse(result.output)).toEqual({
539
+ // No output schema is declared, so the envelope has to travel as content.
540
+ expect(result.output).toBeUndefined();
541
+ expect(JSON.parse(result.content)).toEqual({
470
542
  schemaVersion: 1,
471
543
  ok: true,
472
544
  action: 'projects.list',
473
545
  data: { projects: [] },
474
546
  });
475
- expect(result.title).toBe('List configured projects');
476
547
  expect(result.metadata.openchamber.description).toBe('List configured projects');
477
- expect(metadata).toHaveBeenCalledWith(expect.objectContaining({
478
- title: 'List configured projects',
479
- metadata: expect.objectContaining({
480
- openchamber: expect.objectContaining({ description: 'List configured projects' }),
481
- }),
548
+ expect(progress).toHaveBeenCalledWith(expect.objectContaining({
549
+ openchamber: expect.objectContaining({ description: 'List configured projects' }),
482
550
  }));
483
551
  } finally {
484
552
  if (previousUrl === undefined) delete process.env.OPENCHAMBER_AGENT_TOOL_URL;
@@ -16,9 +16,36 @@ itself; it can only ask and wait.
16
16
  validates the envelope and hands the outcome to the broker.
17
17
  - `../../index.js` supplies `emitRequest`, which writes the request to the
18
18
  OpenChamber SSE clients and returns how many were reached.
19
+ - `provider.js` sits between the control service and the broker. It reads
20
+ the `browserProvider` setting on every action: `builtin` goes to the broker;
21
+ an extension id goes to that extension's service (`contributes.service.provides`
22
+ includes `browser`) as `POST /browser-control` on its loopback through
23
+ `../guests/service.js`, with the open/action timeouts, the response cap, and
24
+ the idle stop from `@openchamber/sdk`. The body also carries `context`
25
+ (`directory`, `sessionId`, each `null` when unknown): the project and chat the
26
+ tool call came from, threaded from the plugin (`contextDirectory`,
27
+ `contextSessionId`) through the control service; the model never types it.
28
+ The answer is parsed with
29
+ `browserProviderResultSchema`; `ok: false` becomes the agent's error, any
30
+ other status or shape is reported as unknown page state. A selected extension
31
+ that cannot serve (`isBrowserProviderGuest`: enabled, fully approved, has the
32
+ role) resets the setting to `builtin`, emits
33
+ `openchamber:browser-provider-reset`, and runs the action in-app. A settings
34
+ or catalog read that fails is not that: the action fails with 503, nothing
35
+ runs anywhere, and the choice stays, because a read error says nothing
36
+ about the extension and an action must never land in a browser the user
37
+ did not pick. `REQUEST_FAILED` (sent, no answer) is reported to the agent
38
+ as "may or may not have run, read the page first", never as unchanged.
39
+ `handleGuestDeactivated` does the same reset when the guest routes pause,
40
+ remove, or withdraw approval from the selected extension.
41
+ When the extension also shows a shared surface (`../guests/surface.js`),
42
+ an action is refused with 409 while the user holds the surface, and every
43
+ action that runs is reported as agent activity so the panel says "Agent is
44
+ working".
19
45
  - `../openchamber-control/service.js` is the only caller. It maps the
20
- `browser.*` actions of the `openchamber_web` tool onto `broker.request()` and
21
- owns their parameter validation.
46
+ `browser.*` actions of the `openchamber_web` tool onto the router's
47
+ `request()` (same signature as the broker) and owns their parameter
48
+ validation.
22
49
  - The client half is `packages/ui/src/lib/browser/controlClient.ts`, which
23
50
  registers the mounted browser pane as the one responder.
24
51
 
@@ -48,3 +75,11 @@ itself; it can only ask and wait.
48
75
  and a missing one silently turns every answer into an agent-visible timeout.
49
76
  - Request payload limits are sized for a page snapshot (visible text plus every
50
77
  interactive element), not for a control message.
78
+ - The provider path never trusts the setting alone: the extension is
79
+ re-checked against the catalog on every action, and the grant list handed to
80
+ the service proxy is the catalog's effective list, so a version that widened
81
+ its permissions is refused until re-approved.
82
+ - The provider's answer shapes are the desktop panel's own
83
+ (`packages/sdk/src/service-providers.ts`); `browser.capture` still returns
84
+ `base64`/`mime` and the control service writes the file, so the agent sees
85
+ the same result whoever took the picture.