@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
@@ -6,13 +6,18 @@ This module exposes OpenChamber to agents as typed OpenCode custom tools. There
6
6
  are two, because controlling sessions and driving a page are separate intents
7
7
  the user can want independently:
8
8
 
9
- - `openchamber` — projects, sessions, worktrees, and scheduled tasks. Enabled
10
- while the persisted `agentControlToolEnabled` setting is not `false`.
9
+ - `openchamber` — projects, sessions, worktrees, scheduled tasks, and showing
10
+ a file to the user (`file.open`). Enabled while the persisted
11
+ `agentControlToolEnabled` setting is not `false`.
11
12
  - `openchamber_web` — looking at and interacting with the page in OpenChamber's
12
13
  browser panel. Enabled while `agentWebToolEnabled` is not `false`.
13
14
 
14
- Both default to on, are toggled in Settings → General → OpenCode CLI, and apply
15
- on the next managed OpenCode restart. Each tool carries only its own actions and
15
+ Both default to on, are toggled in Settings → General → OpenCode CLI, and take
16
+ effect in the running OpenCode within a couple of seconds — OpenChamber rewrites
17
+ the managed config file OpenCode watches (see
18
+ `lib/opencode/managed-config-file.js`). Installs where the user's own
19
+ environment sets `OPENCODE_CONFIG` fall back to `OPENCODE_CONFIG_CONTENT` and
20
+ still need a restart for a toggle. Each tool carries only its own actions and
16
21
  only the parameters those actions use, so turning one off removes its inputs
17
22
  from the schema rather than leaving them visible. The plugin is injected only
18
23
  when OpenChamber launches and owns the OpenCode process, and not at all when
@@ -27,15 +32,19 @@ both settings are `false`.
27
32
  ## Runtime flow
28
33
 
29
34
  1. The OpenChamber HTTP listener binds and publishes its authoritative port.
30
- 2. `prepareManagedOpenCodeEnv()` materializes the plugin under
31
- `<openchamber-data-dir>/agent-tool/` and appends its `file://` URL to
32
- `OPENCODE_CONFIG_CONTENT` without replacing existing plugin entries.
33
- 3. A random per-child token and callback URL are added only to the managed
34
- OpenCode child environment. The URL points at loopback, except when the
35
- listener is bound to one concrete address (`--host <ip>`): that socket does
36
- not answer on loopback, so the URL uses the bound address instead.
35
+ 2. `materializePlugin()` writes the plugin under
36
+ `<openchamber-data-dir>/agent-tool/` and returns its directory; the managed
37
+ config layer lists that directory in `<data-dir>/opencode.managed.json` and
38
+ rewrites the file whenever a tool setting changes.
39
+ 3. `createChildEnv()` adds a random per-child token and the callback URL to the
40
+ managed OpenCode child environment. They are present even while every tool
41
+ is off, so a tool switched on later can call back without a restart. The URL
42
+ points at loopback, except when the listener is bound to one concrete
43
+ address (`--host <ip>`): that socket does not answer on loopback, so the URL
44
+ uses the bound address instead.
37
45
  4. The plugin calls `POST /api/openchamber/agent-tool` with its typed input and
38
- OpenCode's authoritative session directory.
46
+ the session id OpenCode gives the tool; OpenChamber resolves the session's
47
+ directory on its own side.
39
48
  5. The route delegates the fixed action allowlist directly to the shared
40
49
  OpenChamber control service. The CLI uses the same service through its
41
50
  authenticated HTTP adapter, so Goal Mode ordering, wait behavior,
@@ -63,8 +72,11 @@ both settings are `false`.
63
72
  `schedule.list` already returns scheduler status, and enable/disable are one
64
73
  `schedule.toggle` action driven by the `disabled` boolean.
65
74
  - The tool description frames intent: created sessions and scheduled tasks are
66
- user-facing work the user follows up with, never a channel for the agent to
67
- delegate parts of its own current task.
75
+ user-facing work the user follows up with. The agent must not decide on its
76
+ own to delegate parts of its current task, but an explicit user request to
77
+ create, send, or schedule always wins, even when it relates to the current
78
+ task (strict models otherwise read the old unconditional "never delegate" as
79
+ a hard ban and refused user-requested sends).
68
80
  - Optional behavior switches (`worktree`, `goal`, `agent`, `variant`, `wait`)
69
81
  state their default and an explicit "only when the user asks" rule so agents
70
82
  do not invent worktrees, goal mode, or waits the user never requested.
@@ -89,7 +101,12 @@ both settings are `false`.
89
101
  - Inputs map to a fixed action and parameter allowlist. There is no arbitrary
90
102
  CLI, shell, route, or URL forwarding.
91
103
  - Session/worktree deletion and project-path registration are not exposed.
92
- - An aborted tool request propagates an abort signal into the shared service.
104
+ - A cancelled turn aborts the session's in-flight actions. OpenCode 2 gives a
105
+ plugin tool no abort signal and the plugin's request stays open, so the
106
+ server tracks in-flight actions per session and `abortSession(sessionID)` is
107
+ called from the event dispatch in `server/index.js` when the stream reports
108
+ `session.idle` with `aborted: true`; the abort signal then reaches the
109
+ shared service as before. A dropped HTTP request still aborts as well.
93
110
 
94
111
  ## Result contract
95
112
 
@@ -1,5 +1,3 @@
1
- import { pathToFileURL } from 'node:url';
2
- import { appendManagedPlugin } from '../opencode/managed-plugin-config.js';
3
1
  import {
4
2
  OPENCHAMBER_AGENT_TOOL_ACTION_DEFINITIONS,
5
3
  OPENCHAMBER_AGENT_TOOL_ACTIONS,
@@ -11,6 +9,7 @@ import {
11
9
  } from '../openchamber-control/actions.js';
12
10
 
13
11
  const TOOL_SCHEMA_VERSION = 1;
12
+ const PLUGIN_ID = 'openchamber-agent-tool';
14
13
  // Everything either managed tool may ask for; the agent allowlist stays
15
14
  // narrower than the full control surface.
16
15
  const ACTIONS = new Set([...OPENCHAMBER_AGENT_TOOL_ACTIONS, ...OPENCHAMBER_WEB_ACTIONS, ...OPENCHAMBER_MEMORY_ACTIONS]);
@@ -79,6 +78,7 @@ const ALL_PARAMETER_PROPERTIES = {
79
78
  cron: { type: 'string', description: 'Cron expression' },
80
79
  timezone: { type: 'string', description: 'IANA timezone' },
81
80
  disabled: { type: 'boolean', description: 'true disables and false enables; required for schedule.toggle' },
81
+ path: { type: 'string', description: 'File to show for file.open; absolute, or relative to the session directory' },
82
82
  url: { type: 'string', description: 'http(s) URL for browser.open' },
83
83
  selector: { type: 'string', description: 'CSS selector from a browser.snapshot result' },
84
84
  text: { type: 'string', description: 'Visible label to match when no selector is given' },
@@ -108,11 +108,11 @@ const MEMORY_PARAMETER_PROPERTIES = {
108
108
  ...MEMORY_PARAMETER_OVERRIDES,
109
109
  };
110
110
 
111
- const CONTROL_TOOL_DESCRIPTION = "Control OpenChamber projects, sessions, and scheduled tasks on the user's behalf. Sessions and scheduled tasks you create are for the user to follow and interact with; never use this tool to delegate parts of your own current task. Use one action per call. Scope with projectId or directory; omit both to use the current session directory. Session dispatches return immediately by default and you receive no notification when a dispatched session finishes, so never promise to report back on it; the user follows it in OpenChamber; a dispatched session needs no follow-up from you. If the user later asks how it went, use session.messages (add wait to block until it is idle, lastAssistant for just the final answer) — session.send always sends a NEW prompt and never just waits. Set wait only when the user asks or the next step requires the completed result. Session and worktree deletion are unavailable.";
111
+ const CONTROL_TOOL_DESCRIPTION = "Control OpenChamber projects, sessions, and scheduled tasks on the user's behalf. Sessions and scheduled tasks you create are for the user to follow and interact with. Do not decide on your own to hand parts of your current task to another session; when the user asks you to create a session, send a prompt to one, or schedule a task, do it, including when the work relates to your current task. Use one action per call. Scope with projectId or directory; omit both to use the current session directory. Session dispatches return immediately by default and you receive no notification when a dispatched session finishes, so never promise to report back on it; the user follows it in OpenChamber; a dispatched session needs no follow-up from you. If the user later asks how it went, use session.messages (add wait to block until it is idle, lastAssistant for just the final answer) — session.send always sends a NEW prompt and never just waits. Set wait only when the user asks or the next step requires the completed result. Session and worktree deletion are unavailable.";
112
112
 
113
113
  const WEB_TOOL_DESCRIPTION = "Look at and interact with a web page in OpenChamber's browser panel, so you can check your own work rather than describing what you expect. Use one action per call. Open a page, snapshot it to read its text and its interactive elements, then click, type or scroll using the selectors the snapshot returned; snapshots also report any errors the page logged. Pass a selector to browser.snapshot to read one part of a long page. browser.inspect returns computed styles when the question is how something renders. Set viewport to check a layout at mobile, tablet or desktop size. The page runs with the user's real logins, so treat what you see as their live session.";
114
114
 
115
- const MEMORY_TOOL_DESCRIPTION = "Keep what you learn across sessions, so the user does not have to explain the same thing twice. Use one action per call. The session already lists the titles of what is stored. A title is an abbreviation, not the memory: read the entry with memory.read before acting on it, because titles leave out the conditions and exceptions that decide how the memory applies, and the ones that look self-explanatory hide them most often. Save something only when it will still be true in a later session — a stable preference, a project convention, a decision and its reason, or a hard-won pointer. Do not save one-off task state, anything you can read from the code, secrets or credentials, or anything the user asked you not to keep. Choose the scope deliberately: global is about the user and reaches every project, so put a project's conventions in project scope. What you save is shown to the user as unreviewed until they confirm it, so save plainly and say what you saved when it matters.";
115
+ const MEMORY_TOOL_DESCRIPTION = "Keep what you learn across sessions, so the user does not have to explain the same thing twice. Use one action per call. The session already lists the titles of what is stored. A title is an abbreviation, not the memory: read the entry with memory.read once before acting on it (it then stays in your context; do not re-read it on later turns), because titles leave out the conditions and exceptions that decide how the memory applies, and the ones that look self-explanatory hide them most often. Save something only when it will still be true in a later session — a stable preference, a project convention, a decision and its reason, or a hard-won pointer. Do not save one-off task state, anything you can read from the code, secrets or credentials, or anything the user asked you not to keep; when the user explicitly asks you to remember something, save it, unless it is a secret or credential. Choose the scope deliberately: global is about the user and reaches every project, so put a project's conventions in project scope. Save in the moment, without asking first, when the user corrects how you work or states a preference, confirms that a non-obvious approach worked, or when you learn a project fact that took real effort to find. One fact per entry. The user can review and remove what you save, so save when it fits and mention it briefly.";
116
116
 
117
117
  const asNonEmptyString = (value) => {
118
118
  if (typeof value !== 'string') return null;
@@ -157,16 +157,29 @@ const resolveConcreteBoundAddress = (value) => {
157
157
  * keeps the transport, metadata and failure handling identical, which is what
158
158
  * the caller depends on.
159
159
  *
160
+ * OpenCode 2 takes a tool's `input` as plain JSON Schema, so the generated file
161
+ * needs no imports at all — which it must not have, because OpenCode loads the
162
+ * entrypoint with a bare dynamic import and nothing resolves from the generated
163
+ * directory.
164
+ *
160
165
  * The action schema carries `oneOf` only. A node combining `enum` and `oneOf`
161
166
  * is valid JSON Schema, but some OpenAI-compatible gateways reject it and
162
167
  * answer with an empty completion instead of an error, and the `oneOf` branches
163
168
  * are what carry the per-action descriptions the model reads.
164
169
  */
165
- const createToolEntry = ({ name, description, definitions, parameters }) => String.raw` ${name}: {
170
+ const createToolEntry = ({ name, description, definitions, parameters }) => String.raw` tools.add({
171
+ name: ${JSON.stringify(name)},
166
172
  description: ${JSON.stringify(description)},
167
- args: {
168
- action: { type: "string", oneOf: ${JSON.stringify(definitions.map((entry) => ({ const: entry.action, description: entry.description })))}, description: "OpenChamber action to perform" },
169
- parameters: { type: "object", properties: ${JSON.stringify(parameters)}, additionalProperties: false, description: "Inputs for the action; use an empty object when none are needed" },
173
+ input: {
174
+ type: "object",
175
+ properties: {
176
+ action: { type: "string", oneOf: ${JSON.stringify(definitions.map((entry) => ({ const: entry.action, description: entry.description })))}, description: "OpenChamber action to perform" },
177
+ parameters: { type: "object", properties: ${JSON.stringify(parameters)}, additionalProperties: false, description: "Inputs for the action; use an empty object when none are needed" },
178
+ },
179
+ required: ["action"],
180
+ // Models routinely put the inputs next to the action; the schema has to
181
+ // let that reach execute() instead of rejecting the call.
182
+ additionalProperties: true,
170
183
  },
171
184
  async execute(input, context) {
172
185
  // Models routinely put the inputs next to the action instead of inside
@@ -177,21 +190,22 @@ const createToolEntry = ({ name, description, definitions, parameters }) => Stri
177
190
  const args = { ...flattened, ...(parameters ?? {}), action: requestedAction }
178
191
  const actionTitles = ${JSON.stringify(AGENT_TOOL_ACTION_TITLES)}
179
192
  const title = Object.hasOwn(actionTitles, args.action) ? actionTitles[args.action] : args.action
180
- context.metadata({
181
- title,
182
- metadata: {
183
- ${name}: {
184
- schemaVersion: ${TOOL_SCHEMA_VERSION},
185
- action: args.action,
186
- description: title,
187
- },
193
+ const progress = (extra) => context.progress({
194
+ ${name}: {
195
+ schemaVersion: ${TOOL_SCHEMA_VERSION},
196
+ action: args.action,
197
+ description: title,
198
+ ...extra,
188
199
  },
189
200
  })
201
+ await progress()
190
202
  const endpoint = process.env.OPENCHAMBER_AGENT_TOOL_URL
191
203
  const token = process.env.OPENCHAMBER_AGENT_TOOL_TOKEN
204
+ // No output schema is declared, so a result must never carry an output
205
+ // field: OpenCode rejects that with "Tool result declared output
206
+ // without an output schema". The envelope travels as text content.
192
207
  const failure = (payload) => ({
193
- title,
194
- output: JSON.stringify(payload),
208
+ content: JSON.stringify(payload),
195
209
  metadata: { openchamber: { schemaVersion: ${TOOL_SCHEMA_VERSION}, action: args.action, description: title, ok: false } },
196
210
  })
197
211
  if (!endpoint || !token) {
@@ -205,32 +219,25 @@ const createToolEntry = ({ name, description, definitions, parameters }) => Stri
205
219
  authorization: "Bearer " + token,
206
220
  "content-type": "application/json",
207
221
  },
208
- body: JSON.stringify({ input: args, contextDirectory: context.directory, tool: ${JSON.stringify(name)} }),
209
- signal: context.abort,
222
+ // OpenCode 2 no longer hands a tool its session directory, so the
223
+ // session id goes over instead and OpenChamber resolves the
224
+ // directory on its own side. The signal fires when the session is
225
+ // aborted, so the OpenChamber side stops the action too.
226
+ body: JSON.stringify({ input: args, sessionID: context.sessionID, tool: ${JSON.stringify(name)} }),
227
+ signal: context.signal,
210
228
  })
211
- const output = await response.text()
229
+ const content = await response.text()
212
230
  let result = null
213
- try { result = JSON.parse(output) } catch {}
231
+ try { result = JSON.parse(content) } catch {}
214
232
  const valid = result?.schemaVersion === ${TOOL_SCHEMA_VERSION} && typeof result?.ok === "boolean" && typeof result?.action === "string"
215
- context.metadata({
216
- title,
217
- metadata: {
218
- ${name}: {
219
- schemaVersion: ${TOOL_SCHEMA_VERSION},
220
- action: args.action,
221
- description: title,
222
- ok: valid && result.ok === true,
223
- },
224
- },
225
- })
226
- if (valid) return { title, output, metadata: { openchamber: { schemaVersion: ${TOOL_SCHEMA_VERSION}, action: args.action, description: title, ok: result.ok === true } } }
233
+ await progress({ ok: valid && result.ok === true })
234
+ if (valid) return { content, metadata: { openchamber: { schemaVersion: ${TOOL_SCHEMA_VERSION}, action: args.action, description: title, ok: result.ok === true } } }
227
235
  return failure({ schemaVersion: ${TOOL_SCHEMA_VERSION}, ok: false, action: args.action, error: { message: "OpenChamber returned an invalid response", kind: "runtime", status: response.status } })
228
236
  } catch (error) {
229
- if (context.abort.aborted) throw error
230
237
  return failure({ schemaVersion: ${TOOL_SCHEMA_VERSION}, ok: false, action: args.action, error: { message: error instanceof Error ? error.message : String(error), kind: "runtime" } })
231
238
  }
232
239
  },
233
- },
240
+ })
234
241
  `;
235
242
 
236
243
  const createPluginSource = ({ includeControl, includeWeb, includeMemory }) => {
@@ -275,16 +282,27 @@ const createPluginSource = ({ includeControl, includeWeb, includeMemory }) => {
275
282
  }
276
283
  }
277
284
 
278
- export const OpenChamberPlugin = async () => {
279
- exemptCallbackFromProxy()
280
- return {
281
- tool: {
282
- ${entries.join('')} },
283
- }
285
+ export default {
286
+ id: ${JSON.stringify(PLUGIN_ID)},
287
+ setup: async (ctx) => {
288
+ exemptCallbackFromProxy()
289
+ await ctx.tool.transform((tools) => {
290
+ ${entries.join('')} })
291
+ },
284
292
  }
285
293
  `;
286
294
  };
287
295
 
296
+ // A configured plugin has to be a directory carrying a package.json that
297
+ // resolves an entrypoint; OpenCode skips a plain .js path.
298
+ const PLUGIN_PACKAGE_JSON = `${JSON.stringify({
299
+ name: 'openchamber-agent-tool',
300
+ version: '0.0.0',
301
+ private: true,
302
+ type: 'module',
303
+ exports: { '.': './index.js' },
304
+ }, null, 2)}\n`;
305
+
288
306
  export const createAgentToolRuntime = (dependencies) => {
289
307
  const {
290
308
  crypto,
@@ -294,30 +312,51 @@ export const createAgentToolRuntime = (dependencies) => {
294
312
  getActivePort,
295
313
  getActiveHost = () => null,
296
314
  executeAction,
297
- env = process.env,
315
+ resolveSessionDirectory,
298
316
  } = dependencies;
299
- const pluginDirectory = path.join(dataDir, 'agent-tool');
300
- const pluginPath = path.join(pluginDirectory, 'openchamber-plugin.js');
317
+ const pluginRoot = path.join(dataDir, 'agent-tool');
318
+ const pluginDirectory = path.join(pluginRoot, PLUGIN_ID);
319
+ const pluginPath = path.join(pluginDirectory, 'index.js');
320
+ const pluginManifestPath = path.join(pluginDirectory, 'package.json');
301
321
  let activeToken = null;
302
322
 
303
323
  const getConcreteBoundAddress = () => resolveConcreteBoundAddress(getActiveHost());
304
324
 
305
- const prepareManagedOpenCodeEnv = async ({ includeControl = true, includeWeb = true, includeMemory = true } = {}) => {
306
- const port = getActivePort();
307
- if (!Number.isInteger(port) || port <= 0) {
308
- throw new Error('OpenChamber listener port is unavailable for managed tool injection');
309
- }
325
+ /**
326
+ * Write the plugin for the requested tool set and return its directory.
327
+ *
328
+ * Called both before a managed child starts and whenever the tool settings
329
+ * change while it runs, so the source on disk always matches the settings —
330
+ * the running OpenCode reloads the directory it already has configured.
331
+ */
332
+ const materializePlugin = async ({ includeControl = true, includeWeb = true, includeMemory = true } = {}) => {
310
333
  if (!includeControl && !includeWeb && !includeMemory) {
311
334
  throw new Error('At least one OpenChamber managed tool must be enabled to inject the plugin');
312
335
  }
313
336
  await fsPromises.mkdir(pluginDirectory, { recursive: true });
337
+ await fsPromises.writeFile(pluginManifestPath, PLUGIN_PACKAGE_JSON, { mode: 0o600 });
314
338
  await fsPromises.writeFile(pluginPath, createPluginSource({ includeControl, includeWeb, includeMemory }), { mode: 0o600 });
339
+ return pluginDirectory;
340
+ };
341
+
342
+ /**
343
+ * Callback URL and a fresh per-child token for a managed OpenCode process.
344
+ *
345
+ * These are always part of the child environment, including when every tool
346
+ * is currently off: a tool switched on later reaches a process that already
347
+ * knows where and how to call back, so the toggle needs no restart.
348
+ */
349
+ const createChildEnv = () => {
350
+ const port = getActivePort();
351
+ if (!Number.isInteger(port) || port <= 0) {
352
+ throw new Error('OpenChamber listener port is unavailable for managed tool injection');
353
+ }
315
354
  activeToken = crypto.randomBytes(32).toString('base64url');
316
- const pluginUrl = pathToFileURL(pluginPath).href;
355
+ // A listener bound to one concrete address does not answer on loopback,
356
+ // so the callback has to point at the bound address instead.
317
357
  const callbackAddress = getConcreteBoundAddress() || '127.0.0.1';
318
358
  const callbackHost = callbackAddress.includes(':') ? `[${callbackAddress}]` : callbackAddress;
319
359
  return {
320
- OPENCODE_CONFIG_CONTENT: appendManagedPlugin(env.OPENCODE_CONFIG_CONTENT, pluginUrl, 'managed tool'),
321
360
  OPENCHAMBER_AGENT_TOOL_URL: `http://${callbackHost}:${port}/api/openchamber/agent-tool`,
322
361
  OPENCHAMBER_AGENT_TOOL_TOKEN: activeToken,
323
362
  };
@@ -357,8 +396,22 @@ export const createAgentToolRuntime = (dependencies) => {
357
396
  if (typeof executeAction !== 'function') {
358
397
  return createResult({ ok: false, action, error: { message: 'OpenChamber control service is unavailable', kind: 'runtime' } });
359
398
  }
399
+ // OpenCode 2 tools no longer receive a directory, so the plugin sends the
400
+ // session id and the directory is resolved here. An unresolvable session
401
+ // falls through with no directory, exactly like the old "no directory" path.
402
+ let contextDirectory = asNonEmptyString(payload.contextDirectory) ?? undefined;
403
+ const sessionID = asNonEmptyString(payload.sessionID);
404
+ if (!contextDirectory && sessionID && typeof resolveSessionDirectory === 'function') {
405
+ contextDirectory = await Promise.resolve(resolveSessionDirectory(sessionID))
406
+ .then((value) => asNonEmptyString(value) ?? undefined)
407
+ .catch(() => undefined);
408
+ }
360
409
  try {
361
- const data = await executeAction(action, { ...payload.input, action }, payload.contextDirectory, options);
410
+ // The calling session scopes browser actions to that session's page.
411
+ const contextSessionId = asNonEmptyString(payload.contextSessionId) ?? sessionID;
412
+ const data = await executeAction(action, { ...payload.input, action }, contextDirectory, contextSessionId
413
+ ? { ...options, contextSessionId }
414
+ : options);
362
415
  return createResult({ ok: true, action, data });
363
416
  } catch (error) {
364
417
  return createResult({
@@ -378,6 +431,29 @@ export const createAgentToolRuntime = (dependencies) => {
378
431
  }
379
432
  };
380
433
 
434
+ // In-flight actions per session. The plugin forwards OpenCode's abort
435
+ // signal (2.0.12+), which closes its request and aborts the action here.
436
+ // Before that release the request stayed open after the user cancelled the
437
+ // turn, so the server also listens for the cancel on the event stream
438
+ // (`session.idle` with `aborted: true`) and aborts the actions itself.
439
+ const inflightBySession = new Map();
440
+ const trackInflight = (sessionID, controller) => {
441
+ if (!sessionID) return () => {};
442
+ const set = inflightBySession.get(sessionID) ?? new Set();
443
+ set.add(controller);
444
+ inflightBySession.set(sessionID, set);
445
+ return () => {
446
+ set.delete(controller);
447
+ if (set.size === 0) inflightBySession.delete(sessionID);
448
+ };
449
+ };
450
+ const abortSession = (sessionID) => {
451
+ const set = inflightBySession.get(asNonEmptyString(sessionID));
452
+ if (!set) return 0;
453
+ for (const controller of set) controller.abort();
454
+ return set.size;
455
+ };
456
+
381
457
  const registerRoutes = (app, express) => {
382
458
  app.post('/api/openchamber/agent-tool', express.json({ limit: '1mb' }), async (req, res) => {
383
459
  if (!authorize(req)) return res.status(401).json({ error: 'Unauthorized' });
@@ -387,6 +463,7 @@ export const createAgentToolRuntime = (dependencies) => {
387
463
  };
388
464
  req.once('aborted', abortOnDisconnect);
389
465
  res.once('close', abortOnDisconnect);
466
+ const untrack = trackInflight(asNonEmptyString(req.body?.sessionID), controller);
390
467
  try {
391
468
  return res.json(await execute(req.body, { signal: controller.signal }));
392
469
  } catch (error) {
@@ -396,6 +473,7 @@ export const createAgentToolRuntime = (dependencies) => {
396
473
  error: { message: error instanceof Error ? error.message : String(error), kind: 'runtime' },
397
474
  }));
398
475
  } finally {
476
+ untrack();
399
477
  req.off('aborted', abortOnDisconnect);
400
478
  res.off('close', abortOnDisconnect);
401
479
  }
@@ -403,8 +481,11 @@ export const createAgentToolRuntime = (dependencies) => {
403
481
  };
404
482
 
405
483
  return {
406
- prepareManagedOpenCodeEnv,
484
+ pluginDirectory,
485
+ materializePlugin,
486
+ createChildEnv,
407
487
  registerRoutes,
408
488
  execute,
489
+ abortSession,
409
490
  };
410
491
  };