@vantran-se/openchamber-web 1.23.1 → 1.24.4

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 (313) hide show
  1. package/bin/cli.test.js +29 -6
  2. package/bin/lib/cli-http.js +5 -1
  3. package/bin/lib/commands-status.js +5 -0
  4. package/dist/assets/{CodeMirrorCommentWidgets-BOpqXpFU.js → CodeMirrorCommentWidgets-B4a0JkUz.js} +1 -1
  5. package/dist/assets/DiffView-B2CD9wC8.js +4 -0
  6. package/dist/assets/FilesView-C4vMFclc.js +13 -0
  7. package/dist/assets/GitView-Byu2DY7s.js +6 -0
  8. package/dist/assets/GuestAttachDialog-B808E0-d.js +1 -0
  9. package/dist/assets/{JsonTreeView-nd_E8eVM.js → JsonTreeView-VW_7czo3.js} +1 -1
  10. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  11. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  12. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  15. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  16. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  17. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  18. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  21. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  22. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  23. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  24. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  25. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  26. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  30. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  31. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  32. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  33. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  34. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  35. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  36. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  37. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  39. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  40. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  41. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  42. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  48. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  49. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  50. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  51. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  52. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  53. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  54. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  55. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  56. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  57. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  58. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  59. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  60. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  61. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  62. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  63. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  64. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  65. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  66. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  67. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  68. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  69. package/dist/assets/LinearIssuesView-EzGNCGrd.js +1 -0
  70. package/dist/assets/{MarkdownImageGallery-CVYds3sQ.js → MarkdownImageGallery-B2YUDFaB.js} +1 -1
  71. package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +12 -0
  72. package/dist/assets/{OnboardingScreen-Dd2IbPTn.js → OnboardingScreen-CQHBSTEi.js} +1 -1
  73. package/dist/assets/{PatchDiff-nUu5i4bG.js → PatchDiff-DCjGhlYV.js} +1 -1
  74. package/dist/assets/PierreDiffViewer-CEDZNkU2.js +210 -0
  75. package/dist/assets/PlanView-D3x-pTkZ.js +5 -0
  76. package/dist/assets/PluginPane-DEcgDQ9h.js +3 -0
  77. package/dist/assets/SettingsView-CSinuH-1.js +29 -0
  78. package/dist/assets/{SettingsWindow-BccxY81J.js → SettingsWindow-B5CHoJFZ.js} +1 -1
  79. package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +1 -0
  80. package/dist/assets/ToolOutputDialog-tLNWmfcW.js +15 -0
  81. package/dist/assets/{ToolPartDiffPreview-BFVgk0UK.js → ToolPartDiffPreview-CflzmK-x.js} +2 -2
  82. package/dist/assets/{VirtualizedFileDiff-z3_H74UR.js → VirtualizedFileDiff-NU0FHFhT.js} +3 -3
  83. package/dist/assets/{WalkthroughView-BqM9GFbJ.js → WalkthroughView-BZyjhqHv.js} +2 -2
  84. package/dist/assets/appThemeRegistry-DLicw4dH.js +1 -0
  85. package/dist/assets/commitAndPush-BDftrjr6.js +1 -0
  86. package/dist/assets/{computeVirtualFileMetrics-Bdazp9Qo.js → computeVirtualFileMetrics-DlWlEM3x.js} +1 -1
  87. package/dist/assets/de-Bro6_zyl.js +10 -0
  88. package/dist/assets/es-Bb9XQZvd.js +16 -0
  89. package/dist/assets/fr-Dgfzn-IL.js +14 -0
  90. package/dist/assets/{index-eo1-D_7z.js → index-CtQNmqUL.js} +1 -1
  91. package/dist/assets/index-DqaMWNrC.css +1 -0
  92. package/dist/assets/index-Dqc6USl3.js +40 -0
  93. package/dist/assets/{isDiffPlainText-BvKH712-.js → isDiffPlainText-CO4e5J_S.js} +1 -1
  94. package/dist/assets/{isFilePlainText-BdVVPukf.js → isFilePlainText-ue6kCou7.js} +1 -1
  95. package/dist/assets/ja-BYsakGFh.js +16 -0
  96. package/dist/assets/ko-BSaQz9aN.js +16 -0
  97. package/dist/assets/main-B62_pjL5.js +2 -0
  98. package/dist/assets/main-BEdcOI2P.js +896 -0
  99. package/dist/assets/{markdown-shiki.worker-D5fBU-lw.js → markdown-shiki.worker-CahLvHIq.js} +5 -5
  100. package/dist/assets/markdownCore-CK48LG0z.js +5 -0
  101. package/dist/assets/miniChat-LT8Rqofr.js +2 -0
  102. package/dist/assets/mobile-CqarEynh.js +2 -0
  103. package/dist/assets/{patchFileDiff-B7LjppSS.js → patchFileDiff-D7QVlovD.js} +3 -3
  104. package/dist/assets/pl-OIviPdoj.js +16 -0
  105. package/dist/assets/pt-BR-Cltg-E6s.js +16 -0
  106. package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +2 -0
  107. package/dist/assets/renderMobileApp-BC1MHg6g.js +25 -0
  108. package/dist/assets/runtimeEndpointReset-DdrazOVH.js +25 -0
  109. package/dist/assets/{scheduledTasksApi-hceovDei.js → scheduledTasksApi-B2SWMGEU.js} +1 -1
  110. package/dist/assets/shikiHighlight-BPu3JiQs.js +1 -0
  111. package/dist/assets/textMateThemeFromAppTheme-DWqRPo9A.js +1 -0
  112. package/dist/assets/tr-CQI-KPre.js +7 -0
  113. package/dist/assets/uk-CqW7R4DA.js +16 -0
  114. package/dist/assets/useAppFontEffects-ne84JBVL.js +7333 -0
  115. package/dist/assets/useInlineCommentController-CgS0fnw_.js +2 -0
  116. package/dist/assets/useRootScrollLock-Cpf1qoGp.js +2 -0
  117. package/dist/assets/{vendor-base-ui-BY_5qla_.js → vendor-base-ui-CsXYnR5c.js} +1 -1
  118. package/dist/assets/{vendor-codemirror-language-data-DWA1bm1F.js → vendor-codemirror-language-data-Bx2EHc1O.js} +2 -2
  119. package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +1 -0
  120. package/dist/assets/vendor-katex-Cp579UoJ.css +1 -0
  121. package/dist/assets/{vendor-motion-dom-D_o3BTI3.js → vendor-motion-dom-DcCgeG69.js} +1 -1
  122. package/dist/assets/{vendor-remixicon-react-Dil0zKKT.js → vendor-remixicon-react-BDmGmFCA.js} +1 -1
  123. package/dist/assets/vendor-strip-json-comments-5dJlOkP1.js +4 -0
  124. package/dist/assets/{vendor-tanstack-react-virtual-g05Cx3z9.js → vendor-tanstack-react-virtual-ClYN8CzB.js} +1 -1
  125. package/dist/assets/{vendor-tanstack-virtual-core-Dubg10g5.js → vendor-tanstack-virtual-core-BewVQryB.js} +1 -1
  126. package/dist/assets/{vendor-zod-B6551dwG.js → vendor-zod-CgAdXWZO.js} +4 -4
  127. package/dist/assets/{web-components-B_Z64So5.js → web-components-DxX8bZ1o.js} +2 -2
  128. package/dist/assets/zh-CN-CVZG_o_J.js +16 -0
  129. package/dist/assets/zh-TW-D5ZxzCZp.js +16 -0
  130. package/dist/index.html +6 -4
  131. package/dist/mini-chat.html +6 -4
  132. package/dist/mobile.html +6 -4
  133. package/package.json +12 -9
  134. package/server/built-in-extensions/registry.json +4 -0
  135. package/server/index.d.ts +2 -0
  136. package/server/index.js +44 -10
  137. package/server/lib/agent-tool/DOCUMENTATION.md +17 -4
  138. package/server/lib/agent-tool/runtime.js +62 -15
  139. package/server/lib/agent-tool/runtime.test.js +88 -2
  140. package/server/lib/changelog/update-notes.js +2 -1
  141. package/server/lib/changelog/update-notes.test.js +19 -0
  142. package/server/lib/event-stream/DOCUMENTATION.md +23 -0
  143. package/server/lib/event-stream/delta-coalescer.js +0 -0
  144. package/server/lib/event-stream/delta-coalescer.test.js +292 -0
  145. package/server/lib/event-stream/global-hub.js +53 -23
  146. package/server/lib/event-stream/global-hub.test.js +160 -0
  147. package/server/lib/event-stream/global-ws-bridge.js +1 -0
  148. package/server/lib/event-stream/index.js +4 -0
  149. package/server/lib/fs/DOCUMENTATION.md +10 -1
  150. package/server/lib/fs/git-process.test.js +77 -0
  151. package/server/lib/fs/routes.js +58 -5
  152. package/server/lib/fs/routes.test.js +116 -0
  153. package/server/lib/fs/search.js +2 -1
  154. package/server/lib/git/DOCUMENTATION.md +20 -5
  155. package/server/lib/git/routes.js +16 -3
  156. package/server/lib/git/serial-refresh.js +93 -0
  157. package/server/lib/git/serial-refresh.test.js +112 -0
  158. package/server/lib/git/service.js +429 -70
  159. package/server/lib/git/service.test.js +418 -0
  160. package/server/lib/github/pr-status.js +5 -5
  161. package/server/lib/github/pr-status.test.js +1 -1
  162. package/server/lib/github/routes.js +4 -4
  163. package/server/lib/guests/DOCUMENTATION.md +68 -0
  164. package/server/lib/guests/auth-store.js +184 -0
  165. package/server/lib/guests/auth-store.test.js +112 -0
  166. package/server/lib/guests/background.test.js +83 -0
  167. package/server/lib/guests/builtins.js +34 -0
  168. package/server/lib/guests/builtins.test.js +174 -0
  169. package/server/lib/guests/catalog.js +477 -0
  170. package/server/lib/guests/catalog.test.js +390 -0
  171. package/server/lib/guests/clone.js +297 -0
  172. package/server/lib/guests/extract-zip.js +136 -0
  173. package/server/lib/guests/extract-zip.test.js +109 -0
  174. package/server/lib/guests/files.js +308 -0
  175. package/server/lib/guests/files.test.js +262 -0
  176. package/server/lib/guests/grant-scope.js +81 -0
  177. package/server/lib/guests/grant-scope.test.js +75 -0
  178. package/server/lib/guests/host-session.js +73 -0
  179. package/server/lib/guests/host-session.test.js +30 -0
  180. package/server/lib/guests/html-styles.js +6 -0
  181. package/server/lib/guests/html-styles.test.js +45 -0
  182. package/server/lib/guests/html-tokens.js +73 -0
  183. package/server/lib/guests/html-tokens.test.js +53 -0
  184. package/server/lib/guests/install.js +406 -0
  185. package/server/lib/guests/install.test.js +338 -0
  186. package/server/lib/guests/oauth.js +501 -0
  187. package/server/lib/guests/oauth.test.js +481 -0
  188. package/server/lib/guests/persist.js +336 -0
  189. package/server/lib/guests/persist.test.js +288 -0
  190. package/server/lib/guests/request.js +112 -0
  191. package/server/lib/guests/request.test.js +206 -0
  192. package/server/lib/guests/routes.js +838 -0
  193. package/server/lib/guests/service.js +538 -0
  194. package/server/lib/guests/service.test.js +277 -0
  195. package/server/lib/guests/sockets.js +129 -0
  196. package/server/lib/guests/sockets.test.js +110 -0
  197. package/server/lib/guests/ssh-install.test.js +188 -0
  198. package/server/lib/guests/storage.js +68 -0
  199. package/server/lib/guests/storage.test.js +59 -0
  200. package/server/lib/guests/updates.js +294 -0
  201. package/server/lib/guests/updates.test.js +334 -0
  202. package/server/lib/guests/upload.js +50 -0
  203. package/server/lib/guests/upload.test.js +131 -0
  204. package/server/lib/linear/DOCUMENTATION.md +1 -1
  205. package/server/lib/message-queue/DOCUMENTATION.md +10 -1
  206. package/server/lib/message-queue/runtime.js +21 -1
  207. package/server/lib/message-queue/runtime.test.js +52 -5
  208. package/server/lib/notifications/routes.js +5 -0
  209. package/server/lib/openchamber-control/DOCUMENTATION.md +6 -0
  210. package/server/lib/openchamber-sessions/routes.js +62 -29
  211. package/server/lib/openchamber-sessions/routes.test.js +96 -0
  212. package/server/lib/opencode/DOCUMENTATION.md +55 -1
  213. package/server/lib/opencode/bootstrap-runtime.js +1 -0
  214. package/server/lib/opencode/core-routes.js +39 -1
  215. package/server/lib/opencode/feature-routes-runtime.js +15 -0
  216. package/server/lib/opencode/lifecycle-process.test.js +84 -0
  217. package/server/lib/opencode/lifecycle.js +54 -67
  218. package/server/lib/opencode/openchamber-routes.js +72 -14
  219. package/server/lib/opencode/openchamber-routes.test.js +71 -3
  220. package/server/lib/opencode/session-runtime.js +68 -0
  221. package/server/lib/opencode/session-runtime.test.js +43 -0
  222. package/server/lib/opencode/settings-helpers.js +8 -0
  223. package/server/lib/opencode/settings-helpers.test.js +13 -1
  224. package/server/lib/opencode/settings-normalization-runtime.js +2 -0
  225. package/server/lib/opencode/settings-normalization-runtime.test.js +13 -0
  226. package/server/lib/opencode/settings-registry.json +15 -0
  227. package/server/lib/opencode/shutdown-runtime.js +3 -0
  228. package/server/lib/opencode/shutdown-runtime.test.js +24 -0
  229. package/server/lib/opencode/theme-archive.js +53 -0
  230. package/server/lib/opencode/theme-catalog.js +171 -0
  231. package/server/lib/opencode/theme-catalog.test.js +125 -0
  232. package/server/lib/opencode/theme-import.test.js +144 -0
  233. package/server/lib/opencode/theme-runtime.js +100 -28
  234. package/server/lib/opencode/theme-runtime.test.js +15 -0
  235. package/server/lib/permission-auto-accept/DOCUMENTATION.md +8 -0
  236. package/server/lib/permission-auto-accept/runtime.js +13 -0
  237. package/server/lib/permission-auto-accept/runtime.test.js +37 -1
  238. package/server/lib/projects/project-config.js +2 -2
  239. package/server/lib/quota/DOCUMENTATION.md +0 -1
  240. package/server/lib/quota/providers/index.js +0 -7
  241. package/server/lib/realtime-proxy-terminal.test.js +150 -0
  242. package/server/lib/realtime-proxy.js +2 -0
  243. package/server/lib/routing/DOCUMENTATION.md +83 -0
  244. package/server/lib/routing/defaults.js +98 -0
  245. package/server/lib/routing/feature-flag.js +11 -0
  246. package/server/lib/routing/history.js +34 -0
  247. package/server/lib/routing/jev.js +98 -0
  248. package/server/lib/routing/routes.http.test.js +100 -0
  249. package/server/lib/routing/routes.js +81 -0
  250. package/server/lib/routing/runtime.js +217 -0
  251. package/server/lib/routing/runtime.test.js +188 -0
  252. package/server/lib/routing/store.js +249 -0
  253. package/server/lib/routing/store.test.js +96 -0
  254. package/server/lib/scheduled-tasks/runtime.js +2 -2
  255. package/server/lib/scheduled-tasks/runtime.test.js +14 -0
  256. package/server/lib/security/request-security.js +9 -5
  257. package/server/lib/security/request-security.test.js +37 -0
  258. package/server/lib/small-model/DOCUMENTATION.md +14 -2
  259. package/server/lib/small-model/call.js +37 -6
  260. package/server/lib/small-model/call.test.js +182 -0
  261. package/server/lib/terminal/DOCUMENTATION.md +23 -2
  262. package/server/lib/terminal/runtime.js +27 -5
  263. package/server/lib/terminal/runtime.test.js +67 -0
  264. package/server/lib/terminal/shutdown.js +106 -0
  265. package/server/lib/terminal/shutdown.test.js +84 -0
  266. package/server/lib/tts/service.test.js +78 -0
  267. package/server/lib/ui-auth/DOCUMENTATION.md +8 -0
  268. package/server/lib/ui-auth/session-cookie.js +51 -0
  269. package/server/lib/ui-auth/session-cookie.test.js +46 -0
  270. package/server/lib/ui-auth/ui-auth.js +56 -17
  271. package/server/lib/ui-auth/ui-auth.test.js +96 -0
  272. package/dist/assets/DiffView-CyLmb-wF.js +0 -4
  273. package/dist/assets/DirtyBranchSwitchDialog-pu5kcoir.js +0 -1
  274. package/dist/assets/FilesView-WtJB0y_l.js +0 -13
  275. package/dist/assets/GitView-DkNrV0BK.js +0 -6
  276. package/dist/assets/LinearIssuesView-B0H0Otii.js +0 -1
  277. package/dist/assets/MarkdownRendererImpl-DSqVN2d8.js +0 -12
  278. package/dist/assets/PierreDiffViewer-Bsq9z-sw.js +0 -161
  279. package/dist/assets/PlanView-CAFziKH5.js +0 -5
  280. package/dist/assets/SettingsView-BRittavZ.js +0 -28
  281. package/dist/assets/ToolOutputDialog-B0uYaJio.js +0 -15
  282. package/dist/assets/appThemeRegistry-BK0dHKa6.js +0 -1
  283. package/dist/assets/de-c7CYLCAW.js +0 -10
  284. package/dist/assets/es-BKAVPQmQ.js +0 -16
  285. package/dist/assets/fr-Bj1glqfV.js +0 -14
  286. package/dist/assets/index-BYFyOu_D.css +0 -1
  287. package/dist/assets/index-BfwRClDe.js +0 -40
  288. package/dist/assets/ja-5jwSIbK0.js +0 -16
  289. package/dist/assets/ko-BaDbg-Eb.js +0 -16
  290. package/dist/assets/main-CGG2_uHq.js +0 -2
  291. package/dist/assets/main-CcrSaL4J.js +0 -930
  292. package/dist/assets/markdownCore-DsmQr3bt.js +0 -5
  293. package/dist/assets/miniChat-DEaAhP4b.js +0 -2
  294. package/dist/assets/mobile-BC3rsvv0.js +0 -2
  295. package/dist/assets/pl-BFKVBDFu.js +0 -16
  296. package/dist/assets/pt-BR-qKfHvkDL.js +0 -16
  297. package/dist/assets/registerCustomTheme-BBXUCRnG.js +0 -1
  298. package/dist/assets/renderElectronMiniChatApp-CR9d1Mr0.js +0 -2
  299. package/dist/assets/renderMobileApp-Da5dwobZ.js +0 -25
  300. package/dist/assets/runtimeEndpointReset-CLYVRw2H.js +0 -41
  301. package/dist/assets/shikiHighlight-D1Z9G58W.js +0 -1
  302. package/dist/assets/tr-DYXPoEDG.js +0 -7
  303. package/dist/assets/uk-CwAoloR8.js +0 -16
  304. package/dist/assets/useAppFontEffects-DeStGcUT.js +0 -7282
  305. package/dist/assets/useGitComparison-CpxioduU.js +0 -1
  306. package/dist/assets/useInlineCommentController-gmUnJtrb.js +0 -2
  307. package/dist/assets/useRootScrollLock--a3cPz8s.js +0 -1
  308. package/dist/assets/vendor-framer-motion-livSKyyV.js +0 -1
  309. package/dist/assets/zh-CN-D9MG0lqF.js +0 -16
  310. package/dist/assets/zh-TW-1HNK7shh.js +0 -16
  311. package/server/lib/quota/providers/crof.js +0 -96
  312. package/server/lib/quota/providers/crof.test.js +0 -98
  313. /package/dist/assets/{vendor-katex-k3Ppq1Ha.js → vendor-katex-CHD9BdDO.js} +0 -0
package/dist/index.html CHANGED
@@ -509,16 +509,18 @@
509
509
  pointer-events: none;
510
510
  }
511
511
  </style>
512
- <script type="module" crossorigin src="/assets/main-CGG2_uHq.js"></script>
512
+ <script type="module" crossorigin src="/assets/main-B62_pjL5.js"></script>
513
513
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
514
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-B6551dwG.js">
514
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-CgAdXWZO.js">
515
515
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
516
516
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
517
517
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
518
518
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-BJ73u-bH.js">
519
- <link rel="modulepreload" crossorigin href="/assets/index-BfwRClDe.js">
519
+ <link rel="modulepreload" crossorigin href="/assets/index-Dqc6USl3.js">
520
520
  <link rel="modulepreload" crossorigin href="/assets/contextPanelEmbeddedChat-CirXEPJq.js">
521
- <link rel="stylesheet" crossorigin href="/assets/index-BYFyOu_D.css">
521
+ <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
522
+ <link rel="stylesheet" crossorigin href="/assets/index-DqaMWNrC.css">
523
+ <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
522
524
  </head>
523
525
  <body class="h-full bg-background text-foreground">
524
526
  <div id="root" class="h-full">
@@ -74,15 +74,17 @@
74
74
  }
75
75
  </style>
76
76
 
77
- <script type="module" crossorigin src="/assets/miniChat-DEaAhP4b.js"></script>
77
+ <script type="module" crossorigin src="/assets/miniChat-LT8Rqofr.js"></script>
78
78
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
79
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-B6551dwG.js">
79
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-CgAdXWZO.js">
80
80
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
81
81
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
83
83
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-BJ73u-bH.js">
84
- <link rel="modulepreload" crossorigin href="/assets/index-BfwRClDe.js">
85
- <link rel="stylesheet" crossorigin href="/assets/index-BYFyOu_D.css">
84
+ <link rel="modulepreload" crossorigin href="/assets/index-Dqc6USl3.js">
85
+ <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
86
+ <link rel="stylesheet" crossorigin href="/assets/index-DqaMWNrC.css">
87
+ <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
86
88
  </head>
87
89
  <body class="h-full bg-background text-foreground">
88
90
  <div id="root" class="h-full">
package/dist/mobile.html CHANGED
@@ -63,15 +63,17 @@
63
63
  })();
64
64
  </script>
65
65
 
66
- <script type="module" crossorigin src="/assets/mobile-BC3rsvv0.js"></script>
66
+ <script type="module" crossorigin src="/assets/mobile-CqarEynh.js"></script>
67
67
  <link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
68
- <link rel="modulepreload" crossorigin href="/assets/vendor-zod-B6551dwG.js">
68
+ <link rel="modulepreload" crossorigin href="/assets/vendor-zod-CgAdXWZO.js">
69
69
  <link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
70
70
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
71
71
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
72
72
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-BJ73u-bH.js">
73
- <link rel="modulepreload" crossorigin href="/assets/index-BfwRClDe.js">
74
- <link rel="stylesheet" crossorigin href="/assets/index-BYFyOu_D.css">
73
+ <link rel="modulepreload" crossorigin href="/assets/index-Dqc6USl3.js">
74
+ <link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
75
+ <link rel="stylesheet" crossorigin href="/assets/index-DqaMWNrC.css">
76
+ <link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
75
77
  </head>
76
78
  <body class="h-full bg-background text-foreground">
77
79
  <div id="root" class="h-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantran-se/openchamber-web",
3
- "version": "1.23.1",
3
+ "version": "1.24.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
@@ -13,10 +13,11 @@
13
13
  },
14
14
  "scripts": {
15
15
  "dev": "bun run build:watch",
16
- "dev:server": "OPENCHAMBER_RELAY_HOST=${OPENCHAMBER_RELAY_HOST:-off} bun server/index.js --port ${OPENCHAMBER_PORT:-3001}",
17
- "dev:server:watch": "node ./scripts/dev-server-watch.mjs",
18
- "build": "vite build",
19
- "build:watch": "vite build --watch",
16
+ "dev:server": "bun ../../scripts/build-builtin-extensions.mjs && OPENCHAMBER_RELAY_HOST=${OPENCHAMBER_RELAY_HOST:-off} bun server/index.js --port ${OPENCHAMBER_PORT:-3001}",
17
+ "dev:server:watch": "bun ../../scripts/build-builtin-extensions.mjs && node ./scripts/dev-server-watch.mjs",
18
+ "build": "bun ../../scripts/build-builtin-extensions.mjs && vite build",
19
+ "build:watch": "bun ../../scripts/build-builtin-extensions.mjs && vite build --watch",
20
+ "prepack": "bun ../../scripts/build-builtin-extensions.mjs",
20
21
  "type-check": "tsc --noEmit",
21
22
  "lint": "eslint \"./src/**/*.{ts,tsx}\" --config ../../eslint.config.js",
22
23
  "test": "vitest run",
@@ -25,25 +26,27 @@
25
26
  "dependencies": {
26
27
  "@clack/prompts": "^1.1.0",
27
28
  "@octokit/rest": "^22.0.1",
28
- "@opencode-ai/sdk": "1.18.30",
29
+ "@openchamber/sdk": "1.24.2",
30
+ "@opencode-ai/sdk": "1.18.31",
29
31
  "@simplewebauthn/server": "13.3.1",
30
32
  "bun-pty": "^0.4.5",
31
33
  "compression": "^1.8.1",
32
- "cron-parser": "^4.9.0",
34
+ "cron-parser": "^5.10.1",
33
35
  "express": "^5.1.0",
34
36
  "http-proxy-middleware": "^3.0.5",
35
37
  "jose": "^6.1.3",
36
38
  "jsonc-parser": "^3.3.1",
37
39
  "luxon": "^3.5.0",
38
40
  "node-pty": "1.2.0-beta.12",
39
- "openai": "^4.79.0",
41
+ "openai": "^7.18.0",
40
42
  "qrcode-terminal": "^0.12.0",
41
43
  "reflect-metadata": "^0.2.2",
42
44
  "sherpa-onnx-node": "1.12.28",
43
45
  "simple-git": "^3.36.0",
44
46
  "web-push": "^3.6.7",
45
47
  "ws": "^8.18.3",
46
- "yaml": "^2.8.1"
48
+ "yaml": "^2.8.1",
49
+ "zod": "^4.3.6"
47
50
  },
48
51
  "devDependencies": {
49
52
  "@base-ui/react": "^1.4.0",
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": 1,
3
+ "extensions": []
4
+ }
package/server/index.d.ts CHANGED
@@ -32,6 +32,8 @@ export interface StartWebUiServerOptions {
32
32
  exitOnShutdown?: boolean;
33
33
  uiPassword?: string | null;
34
34
  desktopUpdater?: DesktopUpdater;
35
+ /** App-owned built-in resources outside Electron's ASAR archive. */
36
+ builtInExtensionsDir?: string;
35
37
  }
36
38
 
37
39
  export declare function startWebUiServer(
package/server/index.js CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  createGlobalUiEventBroadcaster,
44
44
  createGlobalMessageStreamHub,
45
45
  createMessageStreamWsRuntime,
46
+ resolveDeltaCoalesceWindowMs,
46
47
  DEFAULT_UPSTREAM_STALL_TIMEOUT_MS,
47
48
  UPSTREAM_STALL_TIMEOUT_CONCURRENT_MS,
48
49
  } from './lib/event-stream/index.js';
@@ -92,7 +93,9 @@ import { createApnsRuntime } from './lib/notifications/apns-runtime.js';
92
93
  import { createNotificationTemplateRuntime } from './lib/notifications/template-runtime.js';
93
94
  import { createPermissionAutoAcceptRuntime } from './lib/permission-auto-accept/runtime.js';
94
95
  import { createMessageQueueRuntime } from './lib/message-queue/runtime.js';
96
+ import { createRoutingRuntime } from './lib/routing/runtime.js';
95
97
  import { createGracefulShutdownRuntime } from './lib/opencode/shutdown-runtime.js';
98
+ import { stopAllGuestServices } from './lib/guests/service.js';
96
99
  import { createProjectConfigRuntime } from './lib/projects/project-config.js';
97
100
  import { migrateLegacyUserDirs } from './lib/data-dir-migration.js';
98
101
  import { createProjectContextRuntime } from './lib/project-context/runtime.js';
@@ -297,6 +300,8 @@ const themeRuntime = createThemeRuntime({
297
300
  });
298
301
 
299
302
  const readCustomThemesFromDisk = (...args) => themeRuntime.readCustomThemesFromDisk(...args);
303
+ const saveImportedTheme = (...args) => themeRuntime.saveImportedTheme(...args);
304
+ const deleteImportedTheme = (...args) => themeRuntime.deleteImportedTheme(...args);
300
305
 
301
306
  let notificationTemplateRuntime = null;
302
307
  let agentToolRuntime = null;
@@ -889,6 +894,25 @@ const globalMessageStreamHub = createGlobalMessageStreamHub({
889
894
  buildOpenCodeUrl,
890
895
  getOpenCodeAuthHeaders,
891
896
  upstreamStallTimeoutMs: getUpstreamStallTimeoutMs,
897
+ deltaCoalesceWindowMs: resolveDeltaCoalesceWindowMs(),
898
+ });
899
+
900
+ // OpenChamber-owned events for the UI control stream (SSE) plus the WS fan-out.
901
+ // OpenCode's /global/event proxy cannot carry them.
902
+ const broadcastOpenChamberUiEvent = createGlobalUiEventBroadcaster({
903
+ sseClients: uiOpenChamberEventClients,
904
+ wsClients: uiNotificationWsClients,
905
+ writeSseEvent,
906
+ });
907
+
908
+ // Jev model routing and the permission safety net. Dark unless
909
+ // OPENCHAMBER_ROUTING_ENABLE is set; every failure keeps the user's own model
910
+ // or the auto-accept reply it was asked about.
911
+ const routingRuntime = createRoutingRuntime({
912
+ dataDir: OPENCHAMBER_DATA_DIR,
913
+ buildOpenCodeUrl,
914
+ getOpenCodeAuthHeaders,
915
+ broadcastGlobalUiEvent: broadcastOpenChamberUiEvent,
892
916
  });
893
917
 
894
918
  const permissionAutoAcceptRuntime = createPermissionAutoAcceptRuntime({
@@ -898,6 +922,8 @@ const permissionAutoAcceptRuntime = createPermissionAutoAcceptRuntime({
898
922
  readSettingsFromDiskMigrated,
899
923
  persistSettings,
900
924
  broadcastGlobalUiEvent,
925
+ evaluatePermission: (permission, directory) => routingRuntime.evaluatePermission(permission, directory),
926
+ onPermissionReplied: (permissionId) => routingRuntime.forgetPermission(permissionId),
901
927
  });
902
928
  permissionAutoAcceptRuntime.start();
903
929
  notificationTriggerRuntime.setGetIsSessionAutoAccepting(
@@ -911,13 +937,8 @@ const messageQueueRuntime = createMessageQueueRuntime({
911
937
  buildOpenCodeUrl,
912
938
  getOpenCodeAuthHeaders,
913
939
  sessionKnowledgeRuntime,
914
- // OpenCode's /global/event SSE proxy cannot carry OpenChamber-owned events.
915
- // Use the shared control stream for SSE clients and the existing WS fan-out.
916
- broadcastGlobalUiEvent: createGlobalUiEventBroadcaster({
917
- sseClients: uiOpenChamberEventClients,
918
- wsClients: uiNotificationWsClients,
919
- writeSseEvent,
920
- }),
940
+ broadcastGlobalUiEvent: broadcastOpenChamberUiEvent,
941
+ resolvePromptBody: (body, target) => routingRuntime.resolvePromptBody(body, target),
921
942
  onPromptSent: (sessionId) => sessionRuntime.markUserMessageSent(sessionId),
922
943
  dataDir: OPENCHAMBER_DATA_DIR,
923
944
  });
@@ -1345,14 +1366,14 @@ const resolveMemoryProjectId = createMemoryProjectResolver({
1345
1366
  });
1346
1367
 
1347
1368
  /**
1348
- * Tells open panels that the agent changed what it remembers, so what it just
1369
+ * Tells open panels that the service changed what it remembers, so what it just
1349
1370
  * stored is visible without reopening anything.
1350
1371
  */
1351
1372
  const emitAgentMemoryChangedEvent = (event) => {
1352
1373
  for (const client of uiOpenChamberEventClients) {
1353
1374
  try {
1354
1375
  writeSseEvent(client, {
1355
- type: 'openchamber:agent-memory-changed',
1376
+ type: 'openchamber:service-memory-changed',
1356
1377
  properties: {
1357
1378
  scope: event.scope,
1358
1379
  ...(event.projectId ? { projectId: event.projectId } : {}),
@@ -1378,6 +1399,7 @@ const openChamberSessionService = createOpenChamberSessionService({
1378
1399
  waitForOpenCodeReady,
1379
1400
  emitSessionCreatedEvent,
1380
1401
  sessionKnowledgeRuntime,
1402
+ resolvePromptBody: (body, target) => routingRuntime.resolvePromptBody(body, target),
1381
1403
  });
1382
1404
  // Browser actions are published to whichever OpenChamber clients are connected;
1383
1405
  // the one owning the browser panel answers. `emitRequest` returns the number of
@@ -1526,6 +1548,8 @@ async function main(options = {}) {
1526
1548
  const address = server?.address?.();
1527
1549
  return typeof address === 'object' && address ? address.port : null;
1528
1550
  },
1551
+ // A pipe listener reports a string here, which has no address to bind back to.
1552
+ getActiveHost: () => server?.address?.()?.address ?? null,
1529
1553
  });
1530
1554
  systemPromptRuntime = createSystemPromptRuntime({
1531
1555
  fsPromises,
@@ -1933,6 +1957,8 @@ async function main(options = {}) {
1933
1957
  resolveGitBinaryForSpawn,
1934
1958
  createFsSearchRuntime: createFsSearchRuntimeFactory,
1935
1959
  openchamberDataDir: OPENCHAMBER_DATA_DIR,
1960
+ openchamberVersion: OPENCHAMBER_VERSION,
1961
+ builtInExtensionsDir: options.builtInExtensionsDir,
1936
1962
  openchamberUserConfigRoot: OPENCHAMBER_USER_CONFIG_ROOT,
1937
1963
  managedChatsRoot: OPENCHAMBER_CHATS_DIR,
1938
1964
  normalizeDirectoryPath,
@@ -1940,6 +1966,8 @@ async function main(options = {}) {
1940
1966
  resolveOptionalProjectDirectory,
1941
1967
  validateDirectoryPath,
1942
1968
  readCustomThemesFromDisk,
1969
+ saveImportedTheme,
1970
+ deleteImportedTheme,
1943
1971
  refreshOpenCodeAfterConfigChange,
1944
1972
  getOpenCodeResolutionSnapshot,
1945
1973
  getOpenCodeUpgradeCapability,
@@ -1973,6 +2001,7 @@ async function main(options = {}) {
1973
2001
  writeSseEvent,
1974
2002
  permissionAutoAcceptRuntime,
1975
2003
  messageQueueRuntime,
2004
+ routingRuntime,
1976
2005
  });
1977
2006
 
1978
2007
  const startupPipelineResult = await startupPipelineRuntime.run({
@@ -2077,7 +2106,7 @@ async function main(options = {}) {
2077
2106
  port: managed ? openCodePort : null,
2078
2107
  };
2079
2108
  },
2080
- stop: (shutdownOptions = {}) => {
2109
+ stop: async (shutdownOptions = {}) => {
2081
2110
  realtimeProxyRuntime.stop();
2082
2111
  clearInterval(relayReconcileTimer);
2083
2112
  try {
@@ -2090,6 +2119,11 @@ async function main(options = {}) {
2090
2119
  } catch {
2091
2120
  // best-effort shutdown of the dictation worker
2092
2121
  }
2122
+ // Guest services are child processes; leaving before SIGTERM lands
2123
+ // (and the SIGKILL fallback fires) orphans them on the user's machine.
2124
+ await stopAllGuestServices().catch(() => {
2125
+ // best-effort teardown of guest service processes
2126
+ });
2093
2127
  return gracefulShutdown({ exitProcess: shutdownOptions.exitProcess ?? false });
2094
2128
  }
2095
2129
  };
@@ -30,8 +30,10 @@ both settings are `false`.
30
30
  2. `prepareManagedOpenCodeEnv()` materializes the plugin under
31
31
  `<openchamber-data-dir>/agent-tool/` and appends its `file://` URL to
32
32
  `OPENCODE_CONFIG_CONTENT` without replacing existing plugin entries.
33
- 3. A random per-child token and loopback callback URL are added only to the
34
- managed OpenCode child environment.
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
37
  4. The plugin calls `POST /api/openchamber/agent-tool` with its typed input and
36
38
  OpenCode's authoritative session directory.
37
39
  5. The route delegates the fixed action allowlist directly to the shared
@@ -48,6 +50,9 @@ both settings are `false`.
48
50
  - The tool exposes one shared parameter object rather than repeating parameters
49
51
  in a large per-action union. Action descriptions carry only required inputs,
50
52
  defaults, or one non-obvious semantic detail.
53
+ - The action schema carries `oneOf` and no `enum`. A node combining `enum` and
54
+ `oneOf` is valid JSON Schema, but some OpenAI-compatible gateways reject it
55
+ and answer with an empty completion instead of an error.
51
56
  - Obvious fields rely on their names and JSON types. Parameter descriptions are
52
57
  reserved for formats, dependencies, scope, and behavior that cannot be safely
53
58
  inferred from the field name.
@@ -69,10 +74,18 @@ both settings are `false`.
69
74
 
70
75
  ## Security invariants
71
76
 
72
- - The callback accepts loopback requests only and requires the current
73
- per-child bearer token using a timing-safe comparison.
77
+ - The callback accepts same-machine requests only and requires the current
78
+ per-child bearer token using a timing-safe comparison. Same-machine means a
79
+ loopback source, or, for a listener bound to one concrete address, a source
80
+ equal to that address: the OS sources a local connection to `<ip>` from
81
+ `<ip>`. A wildcard bind keeps the loopback-only rule, and another machine on
82
+ the network always arrives with its own address.
74
83
  - The token is never persisted, logged, returned to the UI, or written into
75
84
  the materialized plugin.
85
+ - The plugin adds the callback host to `NO_PROXY`/`no_proxy` inside the managed
86
+ child when it loads. Without that, an `HTTP_PROXY` in the child's environment
87
+ would receive a non-loopback callback, token included, because `fetch` has no
88
+ per-request way to skip the environment proxy.
76
89
  - Inputs map to a fixed action and parameter allowlist. There is no arbitrary
77
90
  CLI, shell, route, or URL forwarding.
78
91
  - Session/worktree deletion and project-path registration are not exposed.
@@ -129,11 +129,24 @@ const createResult = ({ ok, action, data, error, exitCode }) => ({
129
129
  ...(Number.isInteger(exitCode) ? { exitCode } : {}),
130
130
  });
131
131
 
132
+ // Node reports an IPv4 peer on a dual-stack socket as `::ffff:<ipv4>`.
133
+ const normalizeAddress = (value) => {
134
+ const address = (asNonEmptyString(value) || '').toLowerCase();
135
+ return address.startsWith('::ffff:') ? address.slice('::ffff:'.length) : address;
136
+ };
137
+
132
138
  const isLoopbackAddress = (value) => {
133
- const address = typeof value === 'string' ? value.toLowerCase() : '';
134
- return address === '127.0.0.1'
135
- || address === '::1'
136
- || address === '::ffff:127.0.0.1';
139
+ const address = normalizeAddress(value);
140
+ return address === '127.0.0.1' || address === '::1';
141
+ };
142
+
143
+ const WILDCARD_ADDRESSES = new Set(['0.0.0.0', '::']);
144
+
145
+ // A wildcard listener answers on loopback. A listener bound to one concrete
146
+ // address answers only there, so that address is the only way back in.
147
+ const resolveConcreteBoundAddress = (value) => {
148
+ const address = normalizeAddress(value);
149
+ return address && !WILDCARD_ADDRESSES.has(address) ? address : null;
137
150
  };
138
151
 
139
152
  /**
@@ -143,11 +156,16 @@ const isLoopbackAddress = (value) => {
143
156
  * set, the inputs and the description differ. Generating them from one template
144
157
  * keeps the transport, metadata and failure handling identical, which is what
145
158
  * the caller depends on.
159
+ *
160
+ * The action schema carries `oneOf` only. A node combining `enum` and `oneOf`
161
+ * is valid JSON Schema, but some OpenAI-compatible gateways reject it and
162
+ * answer with an empty completion instead of an error, and the `oneOf` branches
163
+ * are what carry the per-action descriptions the model reads.
146
164
  */
147
- const createToolEntry = ({ name, description, actions, definitions, parameters }) => String.raw` ${name}: {
165
+ const createToolEntry = ({ name, description, definitions, parameters }) => String.raw` ${name}: {
148
166
  description: ${JSON.stringify(description)},
149
167
  args: {
150
- action: { type: "string", enum: ${JSON.stringify(actions)}, oneOf: ${JSON.stringify(definitions.map((entry) => ({ const: entry.action, description: entry.description })))}, description: "OpenChamber action to perform" },
168
+ action: { type: "string", oneOf: ${JSON.stringify(definitions.map((entry) => ({ const: entry.action, description: entry.description })))}, description: "OpenChamber action to perform" },
151
169
  parameters: { type: "object", properties: ${JSON.stringify(parameters)}, additionalProperties: false, description: "Inputs for the action; use an empty object when none are needed" },
152
170
  },
153
171
  async execute(input, context) {
@@ -221,7 +239,6 @@ const createPluginSource = ({ includeControl, includeWeb, includeMemory }) => {
221
239
  entries.push(createToolEntry({
222
240
  name: 'openchamber',
223
241
  description: CONTROL_TOOL_DESCRIPTION,
224
- actions: OPENCHAMBER_AGENT_TOOL_ACTIONS,
225
242
  definitions: OPENCHAMBER_AGENT_TOOL_ACTION_DEFINITIONS,
226
243
  parameters: CONTROL_PARAMETER_PROPERTIES,
227
244
  }));
@@ -230,7 +247,6 @@ const createPluginSource = ({ includeControl, includeWeb, includeMemory }) => {
230
247
  entries.push(createToolEntry({
231
248
  name: 'openchamber_web',
232
249
  description: WEB_TOOL_DESCRIPTION,
233
- actions: OPENCHAMBER_WEB_ACTIONS,
234
250
  definitions: OPENCHAMBER_WEB_ACTION_DEFINITIONS,
235
251
  parameters: WEB_PARAMETER_PROPERTIES,
236
252
  }));
@@ -239,16 +255,33 @@ const createPluginSource = ({ includeControl, includeWeb, includeMemory }) => {
239
255
  entries.push(createToolEntry({
240
256
  name: 'openchamber_memory',
241
257
  description: MEMORY_TOOL_DESCRIPTION,
242
- actions: OPENCHAMBER_MEMORY_ACTIONS,
243
258
  definitions: OPENCHAMBER_MEMORY_ACTION_DEFINITIONS,
244
259
  parameters: MEMORY_PARAMETER_PROPERTIES,
245
260
  }));
246
261
  }
247
262
 
248
- return `export const OpenChamberPlugin = async () => ({
249
- tool: {
250
- ${entries.join('')} },
251
- })
263
+ // The callback carries the per-child token over plain HTTP. With a proxy in
264
+ // the child's environment, fetch would hand a non-loopback callback, token
265
+ // included, to that proxy, and no per-request option turns that off. The
266
+ // exemption is added inside the child because only there is the final
267
+ // NO_PROXY, merged from the shell and server environments, visible.
268
+ return `const exemptCallbackFromProxy = () => {
269
+ const endpoint = process.env.OPENCHAMBER_AGENT_TOOL_URL
270
+ if (!endpoint || !URL.canParse(endpoint)) return
271
+ const host = new URL(endpoint).hostname.replace(/^\\[|\\]$/g, "")
272
+ for (const key of ["NO_PROXY", "no_proxy"]) {
273
+ const entries = (process.env[key] || "").split(",").map((entry) => entry.trim()).filter(Boolean)
274
+ if (!entries.includes(host)) process.env[key] = [...entries, host].join(",")
275
+ }
276
+ }
277
+
278
+ export const OpenChamberPlugin = async () => {
279
+ exemptCallbackFromProxy()
280
+ return {
281
+ tool: {
282
+ ${entries.join('')} },
283
+ }
284
+ }
252
285
  `;
253
286
  };
254
287
 
@@ -259,6 +292,7 @@ export const createAgentToolRuntime = (dependencies) => {
259
292
  path,
260
293
  dataDir,
261
294
  getActivePort,
295
+ getActiveHost = () => null,
262
296
  executeAction,
263
297
  env = process.env,
264
298
  } = dependencies;
@@ -266,6 +300,8 @@ export const createAgentToolRuntime = (dependencies) => {
266
300
  const pluginPath = path.join(pluginDirectory, 'openchamber-plugin.js');
267
301
  let activeToken = null;
268
302
 
303
+ const getConcreteBoundAddress = () => resolveConcreteBoundAddress(getActiveHost());
304
+
269
305
  const prepareManagedOpenCodeEnv = async ({ includeControl = true, includeWeb = true, includeMemory = true } = {}) => {
270
306
  const port = getActivePort();
271
307
  if (!Number.isInteger(port) || port <= 0) {
@@ -278,15 +314,26 @@ export const createAgentToolRuntime = (dependencies) => {
278
314
  await fsPromises.writeFile(pluginPath, createPluginSource({ includeControl, includeWeb, includeMemory }), { mode: 0o600 });
279
315
  activeToken = crypto.randomBytes(32).toString('base64url');
280
316
  const pluginUrl = pathToFileURL(pluginPath).href;
317
+ const callbackAddress = getConcreteBoundAddress() || '127.0.0.1';
318
+ const callbackHost = callbackAddress.includes(':') ? `[${callbackAddress}]` : callbackAddress;
281
319
  return {
282
320
  OPENCODE_CONFIG_CONTENT: appendManagedPlugin(env.OPENCODE_CONFIG_CONTENT, pluginUrl, 'managed tool'),
283
- OPENCHAMBER_AGENT_TOOL_URL: `http://127.0.0.1:${port}/api/openchamber/agent-tool`,
321
+ OPENCHAMBER_AGENT_TOOL_URL: `http://${callbackHost}:${port}/api/openchamber/agent-tool`,
284
322
  OPENCHAMBER_AGENT_TOOL_TOKEN: activeToken,
285
323
  };
286
324
  };
287
325
 
326
+ // The managed child runs on this machine. Reaching a listener bound to one
327
+ // concrete address makes the OS source the connection from that same address,
328
+ // so it stands in for loopback there; any other machine arrives as itself.
329
+ const isSameMachineAddress = (value) => {
330
+ if (isLoopbackAddress(value)) return true;
331
+ const boundAddress = getConcreteBoundAddress();
332
+ return boundAddress !== null && normalizeAddress(value) === boundAddress;
333
+ };
334
+
288
335
  const authorize = (req) => {
289
- if (!activeToken || !isLoopbackAddress(req.socket?.remoteAddress)) return false;
336
+ if (!activeToken || !isSameMachineAddress(req.socket?.remoteAddress)) return false;
290
337
  const header = asNonEmptyString(req.headers?.authorization);
291
338
  if (!header?.startsWith('Bearer ')) return false;
292
339
  const provided = Buffer.from(header.slice(7));
@@ -121,8 +121,8 @@ describe('managed agent tool runtime', () => {
121
121
  const pluginModule = await import(`${pathToFileURL(pluginPath).href}?both=${Date.now()}`);
122
122
  const { tool } = await pluginModule.OpenChamberPlugin();
123
123
 
124
- const controlActions = tool.openchamber.args.action.enum;
125
- const webActions = tool.openchamber_web.args.action.enum;
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);
126
126
  expect(webActions).toContain('browser.open');
127
127
  expect(controlActions).not.toContain('browser.open');
128
128
  expect(webActions).not.toContain('session.create');
@@ -133,6 +133,23 @@ describe('managed agent tool runtime', () => {
133
133
  expect(Object.keys(tool.openchamber.args.parameters.properties)).toContain('sessionId');
134
134
  });
135
135
 
136
+ it('keeps the action schema to one validator keyword', async () => {
137
+ // A node carrying both `enum` and `oneOf` is valid JSON Schema, but some
138
+ // OpenAI-compatible gateways reject it and answer with an empty completion
139
+ // instead of an error. `oneOf` is the keyword that stayed. Its branches
140
+ // carry the per-action descriptions the model reads.
141
+ 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();
146
+
147
+ for (const entry of Object.values(tool)) {
148
+ expect(entry.args.action.oneOf).toBeInstanceOf(Array);
149
+ expect(entry.args.action).not.toHaveProperty('enum');
150
+ }
151
+ });
152
+
136
153
  it('accepts inputs passed beside the action, not only inside parameters', async () => {
137
154
  const { runtime, dataDir } = await createRuntime();
138
155
  const prepared = await runtime.prepareManagedOpenCodeEnv();
@@ -354,6 +371,75 @@ describe('managed agent tool runtime', () => {
354
371
  expect(response.body).toEqual(expect.objectContaining({ ok: true, action: 'projects.list' }));
355
372
  });
356
373
 
374
+ it.each([
375
+ ['0.0.0.0', 'http://127.0.0.1:3901/api/openchamber/agent-tool'],
376
+ ['::', 'http://127.0.0.1:3901/api/openchamber/agent-tool'],
377
+ [null, 'http://127.0.0.1:3901/api/openchamber/agent-tool'],
378
+ ['127.0.0.1', 'http://127.0.0.1:3901/api/openchamber/agent-tool'],
379
+ ['100.100.0.3', 'http://100.100.0.3:3901/api/openchamber/agent-tool'],
380
+ ['fd7a:115c::3', 'http://[fd7a:115c::3]:3901/api/openchamber/agent-tool'],
381
+ ])('points the callback at where a listener bound to %s answers', async (boundAddress, expectedUrl) => {
382
+ const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
383
+ const env = await runtime.prepareManagedOpenCodeEnv();
384
+ expect(env.OPENCHAMBER_AGENT_TOOL_URL).toBe(expectedUrl);
385
+ });
386
+
387
+ it.each([
388
+ ['100.100.0.3', '100.100.0.3', 200],
389
+ ['100.100.0.3', '::ffff:100.100.0.3', 200],
390
+ ['100.100.0.3', '100.100.0.7', 401],
391
+ ['fd7a:115c::3', 'fd7a:115c::3', 200],
392
+ ['fd7a:115c::3', 'fd7a:115c::7', 401],
393
+ ['0.0.0.0', '192.168.1.20', 401],
394
+ ['0.0.0.0', '0.0.0.0', 401],
395
+ [null, '192.168.1.20', 401],
396
+ ])('bound to %s, answers a token-bearing caller from %s with %i', async (boundAddress, remoteAddress, status) => {
397
+ const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
398
+ const env = await runtime.prepareManagedOpenCodeEnv();
399
+ const app = express();
400
+ app.use((req, _res, next) => {
401
+ Object.defineProperty(req.socket, 'remoteAddress', { value: remoteAddress, configurable: true });
402
+ next();
403
+ });
404
+ runtime.registerRoutes(app, express);
405
+
406
+ await request(app)
407
+ .post('/api/openchamber/agent-tool')
408
+ .set('authorization', `Bearer ${env.OPENCHAMBER_AGENT_TOOL_TOKEN}`)
409
+ .send({ input: { action: 'projects.list' } })
410
+ .expect(status);
411
+ });
412
+
413
+ it.each([
414
+ ['http://100.100.0.3:3901/api/openchamber/agent-tool', 'localhost,127.0.0.1', 'localhost,127.0.0.1,100.100.0.3'],
415
+ ['http://[fd7a:115c::3]:3901/api/openchamber/agent-tool', undefined, 'fd7a:115c::3'],
416
+ ['http://100.100.0.3:3901/api/openchamber/agent-tool', '100.100.0.3', '100.100.0.3'],
417
+ ['not a url', 'localhost', 'localhost'],
418
+ ])('keeps the callback %s away from an environment proxy', async (callbackUrl, existing, expected) => {
419
+ const { runtime, dataDir } = await createRuntime();
420
+ await runtime.prepareManagedOpenCodeEnv();
421
+ const keys = ['OPENCHAMBER_AGENT_TOOL_URL', 'NO_PROXY', 'no_proxy'];
422
+ const previous = keys.map((key) => process.env[key]);
423
+ try {
424
+ process.env.OPENCHAMBER_AGENT_TOOL_URL = callbackUrl;
425
+ for (const key of ['NO_PROXY', 'no_proxy']) {
426
+ if (existing === undefined) delete process.env[key];
427
+ else process.env[key] = existing;
428
+ }
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();
432
+
433
+ expect(process.env.NO_PROXY).toBe(expected);
434
+ expect(process.env.no_proxy).toBe(expected);
435
+ } finally {
436
+ keys.forEach((key, index) => {
437
+ if (previous[index] === undefined) delete process.env[key];
438
+ else process.env[key] = previous[index];
439
+ });
440
+ }
441
+ });
442
+
357
443
  it('executes through the materialized plugin and authenticated callback', async () => {
358
444
  let activePort = null;
359
445
  const { runtime, dataDir } = await createRuntime({ getActivePort: () => activePort });
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // The repo publishes `changelog/index.json` on `main`: one object per release,
4
4
  // newest first, with the app notes grouped as New / Improvements / Fixes /
5
- // Misc. This module fetches it and renders the releases between the installed
5
+ // SDK / Misc. This module fetches it and renders the releases between the installed
6
6
  // version (exclusive) and the offered one (inclusive) as the Markdown the
7
7
  // dialog already understands: a `## [x.y.z] - YYYY-MM-DD` header per release,
8
8
  // the release title in bold, the intro, then the groups.
@@ -16,6 +16,7 @@ const GROUPS = [
16
16
  ['new', 'New'],
17
17
  ['improvements', 'Improvements'],
18
18
  ['fixes', 'Fixes'],
19
+ ['sdk', 'SDK'],
19
20
  ['misc', 'Misc'],
20
21
  ];
21
22
 
@@ -37,6 +37,25 @@ A short intro.
37
37
  - Faster.`);
38
38
  });
39
39
 
40
+ it('shows SDK notes between Fixes and Misc and omits an empty SDK group', () => {
41
+ const withSdk = [{
42
+ ...index[0],
43
+ app: { ...index[0].app, sdk: ['Actions: background execution.'], misc: ['Packaging update.'] },
44
+ }];
45
+ expect(renderUpdateNotes(withSdk, '1.2.2', '1.2.3', compare)).toContain(`### Fixes
46
+
47
+ - Chat: no freeze.
48
+
49
+ ### SDK
50
+
51
+ - Actions: background execution.
52
+
53
+ ### Misc
54
+
55
+ - Packaging update.`);
56
+ expect(renderUpdateNotes([{ ...index[0], app: { sdk: [] } }], '1.2.2', '1.2.3', compare)).not.toContain('### SDK');
57
+ });
58
+
40
59
  it('returns null when nothing lies in the range or the payload is not an index', () => {
41
60
  expect(renderUpdateNotes(index, '1.2.3', '1.2.3', compare)).toBe(null);
42
61
  expect(renderUpdateNotes({ entries: [] }, '1.0.0', '9.9.9', compare)).toBe(null);