shadow-claw 1.23.3 → 1.24.0

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 (286) hide show
  1. package/README.md +42 -5
  2. package/bin/assert-version-bump.mjs +60 -38
  3. package/bin/assert-version-bump.test.mjs +74 -11
  4. package/bin/cli.mjs +166 -0
  5. package/bin/cli.test.mjs +60 -0
  6. package/bin/commands/backup.mjs +70 -0
  7. package/bin/commands/clients.mjs +41 -0
  8. package/bin/commands/peer-id.mjs +76 -0
  9. package/bin/commands/peer-id.test.mjs +104 -0
  10. package/bin/commands/send.mjs +51 -0
  11. package/bin/commands/tasks.mjs +59 -0
  12. package/bin/commands/webrtc-listen.mjs +254 -0
  13. package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
  14. package/bin/rolldown.config.test.mjs +401 -8
  15. package/bin/utils/control-client.mjs +243 -0
  16. package/bin/utils/control-client.test.mjs +83 -0
  17. package/bin/utils/ulid.mjs +45 -0
  18. package/bin/utils/webrtc-control-client.mjs +699 -0
  19. package/bin/utils/webrtc-control-client.test.mjs +318 -0
  20. package/dist/electron/main.cjs +1 -1
  21. package/dist/public/AGENTS.md +5 -3
  22. package/dist/public/README.md +42 -5
  23. package/dist/public/agent.worker.js +6 -6
  24. package/dist/public/{app-routes-B0ePT49v.js → app-routes-Bs-riIvr.js} +1 -1
  25. package/dist/public/backup-controller-C_s9in2t.js +1 -0
  26. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
  27. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
  28. package/dist/public/components/settings/shadow-claw-storage/shadow-claw-storage.html +1 -1
  29. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
  30. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  31. package/dist/public/components/shadow-claw-files/shadow-claw-files.css +1 -1
  32. package/dist/public/components/shadow-claw-files/shadow-claw-files.html +1 -1
  33. package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
  34. package/dist/public/config-CrHchneq.js +1 -0
  35. package/dist/public/{configurePeerJs-B7kNU8AQ.js → configurePeerJs-CxpZeDJJ.js} +1 -1
  36. package/dist/public/{connections-xd_66YVU.js → connections-DvbgdkaB.js} +1 -1
  37. package/dist/public/{custom-element-security-CZ92anB8.js → custom-element-security-NCqQ5lmC.js} +1 -1
  38. package/dist/public/docs/README.md +3 -1
  39. package/dist/public/docs/architecture/overview.md +12 -12
  40. package/dist/public/docs/architecture/storage.md +5 -2
  41. package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
  42. package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +6 -3
  43. package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
  44. package/dist/public/docs/example/article/index.html +2 -2
  45. package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
  46. package/dist/public/docs/guides/server-development-configuration.md +24 -0
  47. package/dist/public/docs/publishing/index.html +19 -76
  48. package/dist/public/docs/skill-creator/index.html +2 -2
  49. package/dist/public/docs/subsystems/backup.md +56 -0
  50. package/dist/public/docs/subsystems/cli.md +124 -0
  51. package/dist/public/docs/subsystems/control-plane.md +158 -0
  52. package/dist/public/docs/subsystems/notifications.md +54 -2
  53. package/dist/public/docs/subsystems/reactive-ui.md +1 -0
  54. package/dist/public/downloadGroupFile-By0GR6eV.js +1 -0
  55. package/dist/public/e2e/README.md +5 -1
  56. package/dist/public/{e2e-bridge-DkS5xFGX.js → e2e-bridge-IGWsRl4i.js} +1 -1
  57. package/dist/public/file-viewer-D0eflgt6.js +1 -0
  58. package/dist/public/files/main/~/docs/publishing-to-github-pages.md +17 -82
  59. package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
  60. package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
  61. package/dist/public/{git-BkjrsRXX.js → git-7Vq_SJSp.js} +2 -2
  62. package/dist/public/{iframe-sanitizer-L6Jyu__Z.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
  63. package/dist/public/index.html +2 -2
  64. package/dist/public/index.js +1 -1
  65. package/dist/public/{initChatSplitResize-BslPXAXS.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
  66. package/dist/public/initControlPlane-C9jpnWev.js +1 -0
  67. package/dist/public/llms.txt +1 -1
  68. package/dist/public/main/index.html +2 -2
  69. package/dist/public/main/memory/index.html +2 -2
  70. package/dist/public/{markdown-6MBrIo-S.js → markdown-BnpKxSxl.js} +1 -1
  71. package/dist/public/mcp-reconnect-9emsqGRe.js +1 -0
  72. package/dist/public/{memoryStorage-CjGseKI1.js → memoryStorage-C0KvLNUp.js} +1 -1
  73. package/dist/public/{orchestrator-B9KmIj8Q.js → orchestrator-3h9xxHUm.js} +676 -676
  74. package/dist/public/orchestrator-nJj59RqD.js +15 -0
  75. package/dist/public/pages/main/~/docs/publishing-to-github-pages.md +17 -82
  76. package/dist/public/{peerjs-Dv8JNm7v.js → peerjs-BP4laMXj.js} +1 -1
  77. package/dist/public/{push-client-Dqq06wbB.js → push-client-CrjEXK3U.js} +1 -1
  78. package/dist/public/service-worker/fetch-proxy.js +1 -1
  79. package/dist/public/service-worker/push-handler.js +1 -1
  80. package/dist/public/service-worker.js +1 -1
  81. package/dist/public/{shadow-claw-Bd1rfuVC.js → shadow-claw-CbwETBWf.js} +2 -2
  82. package/dist/public/{shadow-claw-accounts-DL2l3oM0.js → shadow-claw-accounts-B4sBTQEj.js} +3 -3
  83. package/dist/public/{shadow-claw-channels-BGXDsQ38.js → shadow-claw-channels-BDWG5Qa3.js} +1 -1
  84. package/dist/public/{shadow-claw-chat-NXg6zz_l.js → shadow-claw-chat-Crv0DTCY.js} +6 -6
  85. package/dist/public/shadow-claw-control-plane-CX1IGbh9.js +199 -0
  86. package/dist/public/{shadow-claw-conversations-C8eqGpP9.js → shadow-claw-conversations-_9qAaRXk.js} +1 -1
  87. package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
  88. package/dist/public/shadow-claw-file-viewer-Blf6yGVD.js +577 -0
  89. package/dist/public/shadow-claw-files-CYySBDCl.js +840 -0
  90. package/dist/public/{shadow-claw-git-C0ntz7aX.js → shadow-claw-git-v8S0JjuR.js} +3 -3
  91. package/dist/public/{shadow-claw-integrations-DCXV2r69.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
  92. package/dist/public/{shadow-claw-llm-CudgFQsS.js → shadow-claw-llm-GWgoVfhI.js} +2 -2
  93. package/dist/public/{shadow-claw-mcp-remote-B8RkpX28.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
  94. package/dist/public/{shadow-claw-networking-BLiRsrsK.js → shadow-claw-networking-qHbP95z9.js} +1 -1
  95. package/dist/public/{shadow-claw-notifications-CQhQcjtL.js → shadow-claw-notifications-YyHOK2Fr.js} +1 -1
  96. package/dist/public/{shadow-claw-page-header-NeRD0tKU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
  97. package/dist/public/{shadow-claw-pages-DCHnFh6r.js → shadow-claw-pages-DFZHhzFZ.js} +3 -3
  98. package/dist/public/{shadow-claw-settings-VAfjKFQg.js → shadow-claw-settings-DhxrKIfA.js} +10 -3
  99. package/dist/public/shadow-claw-storage-BFynbP0j.js +219 -0
  100. package/dist/public/{shadow-claw-task-server-Dk33N_1G.js → shadow-claw-task-server-3BIeb5oZ.js} +25 -4
  101. package/dist/public/{shadow-claw-tasks-xA1VfmWN.js → shadow-claw-tasks-Caxc9atL.js} +4 -4
  102. package/dist/public/{shadow-claw-terminal-Du2r0ciq.js → shadow-claw-terminal-Beb5qF-U.js} +1 -1
  103. package/dist/public/{shadow-claw-tools-CZj4rq7l.js → shadow-claw-tools-BfJx3A9W.js} +2 -2
  104. package/dist/public/{shadow-claw-webvm-JYUkWICG.js → shadow-claw-webvm-BCS8ykeP.js} +1 -1
  105. package/dist/public/static-main/~/docs/publishing-to-github-pages.md +17 -82
  106. package/dist/public/static-main-manifest.json +1 -1
  107. package/dist/public/{syncWebMcpRegistration-DfpvASnh.js → syncWebMcpRegistration-B_M2fCjH.js} +1 -1
  108. package/dist/server.js +286 -189
  109. package/package.json +2 -1
  110. package/src/__mocks__/jszip.cjs +15 -5
  111. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
  112. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
  113. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
  114. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
  115. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
  116. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
  117. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
  118. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
  119. package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
  120. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
  121. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
  122. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
  123. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
  124. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.html +17 -0
  125. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +120 -0
  126. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.ts +56 -0
  127. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
  128. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  129. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
  130. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
  131. package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
  132. package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
  133. package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
  134. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
  135. package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
  136. package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
  137. package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
  138. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
  139. package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
  140. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +378 -0
  141. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +11 -9
  142. package/src/components/shadow-claw-files/shadow-claw-files.css +41 -17
  143. package/src/components/shadow-claw-files/shadow-claw-files.html +35 -0
  144. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +627 -1
  145. package/src/components/shadow-claw-files/shadow-claw-files.ts +170 -6
  146. package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.test.ts +31 -0
  147. package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.ts +2 -1
  148. package/src/components/shadow-claw-pages/utils/rewriteWorkspacePreviewHtml.test.ts +15 -0
  149. package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
  150. package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
  151. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
  152. package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
  153. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
  154. package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
  155. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
  156. package/src/config/config.test.ts +9 -0
  157. package/src/config/config.ts +15 -0
  158. package/src/core/app-routes.test.ts +56 -0
  159. package/src/core/app-routes.ts +8 -1
  160. package/src/core/backup-controller.test.ts +101 -0
  161. package/src/core/backup-controller.ts +137 -0
  162. package/src/core/control-plane-client.test.ts +408 -0
  163. package/src/core/control-plane-client.ts +425 -0
  164. package/src/core/orchestrator/orchestrator.ts +4 -0
  165. package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
  166. package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
  167. package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
  168. package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
  169. package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
  170. package/src/core/utils/initControlPlane.test.ts +276 -0
  171. package/src/core/utils/initControlPlane.ts +437 -0
  172. package/src/core/utils/initializeApp.ts +18 -0
  173. package/src/db/getSession.test.ts +13 -1
  174. package/src/db/openDatabase.test.ts +10 -1
  175. package/src/db/openDatabase.ts +3 -1
  176. package/src/server/app.test.ts +12 -0
  177. package/src/server/app.ts +18 -8
  178. package/src/server/client-registry.test.ts +250 -0
  179. package/src/server/client-registry.ts +477 -0
  180. package/src/server/config.ts +15 -0
  181. package/src/server/control-plane-types.ts +120 -0
  182. package/src/server/control-plane.test.ts +472 -0
  183. package/src/server/control-plane.ts +581 -0
  184. package/src/server/routes/backup.test.ts +263 -0
  185. package/src/server/routes/backup.ts +262 -0
  186. package/src/server/routes/integrations-email.test.ts +532 -0
  187. package/src/server/routes/llamafile.test.ts +172 -48
  188. package/src/server/routes/mesh-llm.test.ts +192 -0
  189. package/src/server/routes/ollama.test.ts +49 -0
  190. package/src/server/server-peer.test.ts +86 -0
  191. package/src/server/server-peer.ts +330 -0
  192. package/src/server/server.ts +35 -0
  193. package/src/server/services/llamafile-manager.test.ts +230 -6
  194. package/src/server/services/transformers-runtime.test.ts +186 -18
  195. package/src/server/webrtc.test.ts +21 -0
  196. package/src/server/webrtc.ts +72 -0
  197. package/src/service-worker/fetch-proxy.test.ts +64 -1
  198. package/src/service-worker/push-handler.ts +39 -0
  199. package/src/shell/vm.test.ts +744 -12
  200. package/src/storage/ensureMainGroupIndex.test.ts +153 -0
  201. package/src/storage/getGroupFile.test.ts +66 -0
  202. package/src/storage/memoryStorage.test.ts +82 -2
  203. package/src/storage/migrateLegacyOpfs.test.ts +327 -0
  204. package/src/storage/migrateLegacyOpfs.ts +117 -0
  205. package/src/storage/writeGroupFileBytes.test.ts +143 -0
  206. package/src/stores/chat-ui.test.ts +20 -0
  207. package/src/stores/orchestrator.test.ts +134 -0
  208. package/src/stores/toast.test.ts +50 -0
  209. package/src/stores/tools.test.ts +147 -10
  210. package/src/subsystems/channels/imessage.test.ts +59 -0
  211. package/src/subsystems/channels/peerjs.test.ts +500 -62
  212. package/src/subsystems/channels/peerjs.ts +71 -4
  213. package/src/subsystems/channels/telegram.test.ts +177 -0
  214. package/src/subsystems/email/catalog.test.ts +1 -0
  215. package/src/subsystems/email/connections.test.ts +39 -1
  216. package/src/subsystems/git/credentials.test.ts +160 -825
  217. package/src/subsystems/git/git.test.ts +463 -0
  218. package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
  219. package/src/subsystems/notifications/push-routes.test.ts +33 -0
  220. package/src/subsystems/notifications/push-routes.ts +23 -0
  221. package/src/subsystems/notifications/push-store.ts +1 -1
  222. package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
  223. package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
  224. package/src/subsystems/providers/model-registry.test.ts +115 -0
  225. package/src/subsystems/providers/providers.test.ts +94 -1
  226. package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
  227. package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
  228. package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
  229. package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
  230. package/src/subsystems/skills/activateSkill.test.ts +101 -0
  231. package/src/subsystems/skills/discoverSkills.test.ts +251 -0
  232. package/src/subsystems/tools/cron.test.ts +64 -0
  233. package/src/subsystems/tools/declarative.test.ts +255 -89
  234. package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
  235. package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
  236. package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
  237. package/src/testing/jest-setup.ts +328 -40
  238. package/src/testing/mock-v86.mjs +49 -0
  239. package/src/ui/a2ui/types.ts +4 -3
  240. package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
  241. package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
  242. package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
  243. package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
  244. package/src/worker/tools/email/email.test.ts +193 -8
  245. package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
  246. package/src/worker/tools/git/git.test.ts +214 -3
  247. package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
  248. package/src/worker/tools/rooms/leave-room.test.ts +40 -0
  249. package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
  250. package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
  251. package/src/worker/tools/ui/ask-user.test.ts +50 -0
  252. package/src/worker/tools/ui/clear-chat.test.ts +19 -0
  253. package/src/worker/tools/ui/get-current-time.test.ts +19 -0
  254. package/src/worker/tools/ui/javascript.test.ts +116 -0
  255. package/src/worker/tools/ui/list-components.test.ts +10 -0
  256. package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
  257. package/src/worker/tools/ui/manage-tools.test.ts +34 -0
  258. package/src/worker/tools/ui/render-component.test.ts +346 -0
  259. package/src/worker/tools/ui/send-notification.test.ts +40 -0
  260. package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
  261. package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
  262. package/src/worker/tools/workspace/attach-file.test.ts +84 -0
  263. package/src/worker/tools/workspace/copy-file.test.ts +69 -0
  264. package/src/worker/tools/workspace/create-directory.test.ts +40 -0
  265. package/src/worker/tools/workspace/delete-file.test.ts +73 -0
  266. package/src/worker/tools/workspace/diff-files.test.ts +94 -0
  267. package/src/worker/tools/workspace/list-files.test.ts +36 -0
  268. package/src/worker/tools/workspace/move-file.test.ts +66 -0
  269. package/src/worker/tools/workspace/open-file.test.ts +57 -0
  270. package/src/worker/tools/workspace/patch-file.test.ts +81 -0
  271. package/src/worker/tools/workspace/read-file.test.ts +116 -0
  272. package/src/worker/tools/workspace/send-file.test.ts +82 -0
  273. package/src/worker/tools/workspace/update-memory.test.ts +29 -0
  274. package/src/worker/tools/workspace/write-file.test.ts +28 -0
  275. package/src/worker/utils/rate-limit.test.ts +199 -0
  276. package/src/worker/utils/tool-state.test.ts +29 -0
  277. package/src/worker/utils/transformers-js-utils.test.ts +59 -0
  278. package/dist/public/config-AL77QAx4.js +0 -1
  279. package/dist/public/downloadGroupFile-BABv_1mJ.js +0 -1
  280. package/dist/public/file-viewer-CCSABMsE.js +0 -1
  281. package/dist/public/mcp-reconnect-DoicazQd.js +0 -1
  282. package/dist/public/orchestrator-4su7iyWj.js +0 -15
  283. package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
  284. package/dist/public/shadow-claw-file-viewer-9Qkwc30T.js +0 -577
  285. package/dist/public/shadow-claw-files-aDyq6Hv7.js +0 -781
  286. package/dist/public/shadow-claw-storage-Cf31np81.js +0 -202
@@ -0,0 +1,75 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../../db/rooms.js", () => ({
4
+ ROOM_PREFIX: "room:",
5
+ roomIdFromGroupId: (g: string) => g.replace(/^room:/, ""),
6
+ getRoomMetadata: jest.fn(),
7
+ }));
8
+
9
+ const { executeListRoomMembers } = await import("./list-room-members.js");
10
+ const { getRoomMetadata } = await import("../../../db/rooms.js");
11
+
12
+ describe("executeListRoomMembers", () => {
13
+ const mockDb: any = {};
14
+
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+
19
+ it("returns error when room_id is missing and groupId is not a room", async () => {
20
+ const result = await executeListRoomMembers(mockDb, {}, "not-a-room");
21
+ expect(result).toBe(
22
+ "Error: no room_id provided and the current conversation is not a room.",
23
+ );
24
+ });
25
+
26
+ it("returns error when room is not found in database", async () => {
27
+ (getRoomMetadata as jest.Mock<any>).mockResolvedValue([]);
28
+
29
+ const result = await executeListRoomMembers(
30
+ mockDb,
31
+ { room_id: "missing-room" },
32
+ "default",
33
+ );
34
+ expect(result).toBe("Error: room missing-room was not found.");
35
+ });
36
+
37
+ it("handles room with no members", async () => {
38
+ (getRoomMetadata as jest.Mock<any>).mockResolvedValue([
39
+ { roomId: "room-1", name: "Empty Room", members: [] },
40
+ ]);
41
+
42
+ const result = await executeListRoomMembers(
43
+ mockDb,
44
+ { room_id: "room-1" },
45
+ "default",
46
+ );
47
+ expect(result).toBe('Room "Empty Room" (room-1) has no members yet.');
48
+ });
49
+
50
+ it("formats members list including agents and hosts", async () => {
51
+ (getRoomMetadata as jest.Mock<any>).mockResolvedValue([
52
+ {
53
+ roomId: "room-1",
54
+ name: "Dev Team",
55
+ hostPeerId: "peer-host",
56
+ members: [
57
+ { peerId: "peer-host", alias: "Host Alice", kind: "human" },
58
+ {
59
+ peerId: "peer-bot",
60
+ alias: "Assistant Bot",
61
+ kind: "agent",
62
+ agentName: "coder",
63
+ },
64
+ ],
65
+ },
66
+ ]);
67
+
68
+ const result = await executeListRoomMembers(mockDb, {}, "room:room-1");
69
+ expect(result).toContain('Room "Dev Team" (room-1) members:');
70
+ expect(result).toContain("- Host Alice — human [host] (peer: peer-host)");
71
+ expect(result).toContain(
72
+ "- Assistant Bot — agent (@coder) (peer: peer-bot)",
73
+ );
74
+ });
75
+ });
@@ -0,0 +1,22 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../../db/getAllTasks.js", () => ({
4
+ getAllTasks: jest.fn(),
5
+ }));
6
+
7
+ const { getGroupTasks } = await import("./tasks-utils.js");
8
+ const { getAllTasks } = await import("../../../db/getAllTasks.js");
9
+
10
+ describe("getGroupTasks", () => {
11
+ it("filters tasks by groupId", async () => {
12
+ (getAllTasks as jest.Mock<any>).mockResolvedValue([
13
+ { id: "task-1", groupId: "group-a", title: "Task 1" },
14
+ { id: "task-2", groupId: "group-b", title: "Task 2" },
15
+ { id: "task-3", groupId: "group-a", title: "Task 3" },
16
+ ]);
17
+
18
+ const result = await getGroupTasks({} as any, "group-a");
19
+ expect(result).toHaveLength(2);
20
+ expect(result.map((t) => t.id)).toEqual(["task-1", "task-3"]);
21
+ });
22
+ });
@@ -0,0 +1,50 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../utils/post.js", () => ({
4
+ post: jest.fn(),
5
+ }));
6
+
7
+ jest.unstable_mockModule("../../../utils/ulid.js", () => ({
8
+ ulid: jest.fn(() => "mock-ask-id"),
9
+ }));
10
+
11
+ const { executeAskUser } = await import("./ask-user.js");
12
+ const { post } = await import("../../utils/post.js");
13
+
14
+ describe("executeAskUser", () => {
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ (globalThis as any).pendingAskUserResolvers = {};
18
+ });
19
+
20
+ it("returns error when question is missing", async () => {
21
+ const result = await executeAskUser({}, "group-1");
22
+ expect(result).toBe("Error: question is required.");
23
+ expect(post).not.toHaveBeenCalled();
24
+ });
25
+
26
+ it("posts ask-user message and resolves when resolver is invoked", async () => {
27
+ const askPromise = executeAskUser(
28
+ { question: "Do you want to proceed?", options: ["Yes", "No"] },
29
+ "group-1",
30
+ );
31
+
32
+ expect(post).toHaveBeenCalledWith({
33
+ type: "ask-user",
34
+ payload: {
35
+ id: "mock-ask-id",
36
+ groupId: "group-1",
37
+ question: "Do you want to proceed?",
38
+ options: ["Yes", "No"],
39
+ },
40
+ });
41
+
42
+ const resolver = (globalThis as any).pendingAskUserResolvers["mock-ask-id"];
43
+ expect(typeof resolver).toBe("function");
44
+
45
+ resolver("User selected: Yes");
46
+
47
+ const result = await askPromise;
48
+ expect(result).toBe("User selected: Yes");
49
+ });
50
+ });
@@ -0,0 +1,19 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../utils/post.js", () => ({
4
+ post: jest.fn(),
5
+ }));
6
+
7
+ const { executeClearChat } = await import("./clear-chat.js");
8
+ const { post } = await import("../../utils/post.js");
9
+
10
+ describe("executeClearChat", () => {
11
+ it("posts clear-chat message and returns confirmation", () => {
12
+ const result = executeClearChat("group-123");
13
+ expect(result).toContain("Chat history cleared successfully");
14
+ expect(post).toHaveBeenCalledWith({
15
+ type: "clear-chat",
16
+ payload: { groupId: "group-123" },
17
+ });
18
+ });
19
+ });
@@ -0,0 +1,19 @@
1
+ import { executeGetCurrentTime } from "./get-current-time.js";
2
+
3
+ describe("executeGetCurrentTime", () => {
4
+ it("returns ISO string when timezone is not provided", () => {
5
+ const result = executeGetCurrentTime({});
6
+ expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
7
+ });
8
+
9
+ it("returns formatted date and time when valid timezone is provided", () => {
10
+ const result = executeGetCurrentTime({ timezone: "America/New_York" });
11
+ expect(typeof result).toBe("string");
12
+ expect(result.length).toBeGreaterThan(0);
13
+ });
14
+
15
+ it("returns error string when invalid timezone is provided", () => {
16
+ const result = executeGetCurrentTime({ timezone: "NonExistent/Timezone" });
17
+ expect(result).toContain("Error: Invalid timezone NonExistent/Timezone");
18
+ });
19
+ });
@@ -0,0 +1,116 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../utils/sandboxedEval.js", () => ({
4
+ sandboxedEval: jest.fn(),
5
+ }));
6
+
7
+ jest.unstable_mockModule("./utils/getAllowFullInternetAccess.js", () => ({
8
+ getAllowFullInternetAccess: jest.fn<any>().mockResolvedValue(false),
9
+ }));
10
+
11
+ const { executeJavascript } = await import("./javascript.js");
12
+ const { sandboxedEval } = await import("../../utils/sandboxedEval.js");
13
+
14
+ describe("executeJavascript", () => {
15
+ const mockDb: any = {};
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ it("evaluates simple expression returning number", async () => {
22
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
23
+ ok: true,
24
+ value: 42,
25
+ });
26
+
27
+ const result = await executeJavascript(mockDb, { code: "1 + 41" });
28
+ expect(result).toBe("42");
29
+ expect(sandboxedEval).toHaveBeenCalledWith(
30
+ "return (1 + 41);",
31
+ undefined,
32
+ false,
33
+ undefined,
34
+ );
35
+ });
36
+
37
+ it("handles explicit return statements", async () => {
38
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
39
+ ok: true,
40
+ value: "hello",
41
+ });
42
+
43
+ const result = await executeJavascript(mockDb, {
44
+ code: "const x = 'hello'; return x;",
45
+ });
46
+ expect(result).toBe("hello");
47
+ expect(sandboxedEval).toHaveBeenCalledWith(
48
+ "const x = 'hello'; return x;",
49
+ undefined,
50
+ false,
51
+ undefined,
52
+ );
53
+ });
54
+
55
+ it("handles object return values by formatting as JSON", async () => {
56
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
57
+ ok: true,
58
+ value: { name: "Alice", age: 30 },
59
+ });
60
+
61
+ const result = await executeJavascript(mockDb, {
62
+ code: "({ name: 'Alice', age: 30 })",
63
+ });
64
+ expect(JSON.parse(result)).toEqual({ name: "Alice", age: 30 });
65
+ });
66
+
67
+ it("returns null representation for null values", async () => {
68
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
69
+ ok: true,
70
+ value: null,
71
+ });
72
+
73
+ const result = await executeJavascript(mockDb, { code: "null" });
74
+ expect(result).toBe("null");
75
+ });
76
+
77
+ it("provides hint when evaluation returns undefined", async () => {
78
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
79
+ ok: true,
80
+ value: undefined,
81
+ });
82
+
83
+ const result = await executeJavascript(mockDb, { code: "const a = 1;" });
84
+ expect(result).toContain("(no return value)");
85
+ expect(result).toContain("Hint: Your code did not return a value");
86
+ });
87
+
88
+ it("reports JavaScript errors", async () => {
89
+ (sandboxedEval as jest.Mock<any>).mockResolvedValue({
90
+ ok: false,
91
+ error: "ReferenceError: foo is not defined",
92
+ });
93
+
94
+ const result = await executeJavascript(mockDb, { code: "foo" });
95
+ expect(result).toBe("JavaScript error: ReferenceError: foo is not defined");
96
+ });
97
+
98
+ it("falls back to raw code when wrapping in return produces syntax error", async () => {
99
+ (sandboxedEval as jest.Mock<any>)
100
+ .mockResolvedValueOnce({
101
+ ok: false,
102
+ error: "SyntaxError: Unexpected token 'for'",
103
+ })
104
+ .mockResolvedValueOnce({
105
+ ok: true,
106
+ value: undefined,
107
+ });
108
+
109
+ const result = await executeJavascript(mockDb, {
110
+ code: "for (let i = 0; i < 5; i++) { /* do work */ }",
111
+ });
112
+
113
+ expect(sandboxedEval).toHaveBeenCalledTimes(2);
114
+ expect(result).toContain("(no return value)");
115
+ });
116
+ });
@@ -0,0 +1,10 @@
1
+ import { BASIC_CATALOG_REFERENCE } from "../../../ui/a2ui/types.js";
2
+ import { executeListComponents } from "./list-components.js";
3
+
4
+ describe("executeListComponents", () => {
5
+ it("returns the basic catalog reference string", () => {
6
+ const result = executeListComponents();
7
+ expect(result).toBe(BASIC_CATALOG_REFERENCE);
8
+ expect(result).toContain("A2UI Basic Catalog");
9
+ });
10
+ });
@@ -0,0 +1,42 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../../db/getConfig.js", () => ({
4
+ getConfig: jest.fn(),
5
+ }));
6
+
7
+ const { executeListToolProfiles } = await import("./list-tool-profiles.js");
8
+ const { getConfig } = await import("../../../db/getConfig.js");
9
+
10
+ describe("executeListToolProfiles", () => {
11
+ const mockDb: any = {};
12
+
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+
17
+ it("lists default profile when no custom profiles in DB", async () => {
18
+ (getConfig as jest.Mock<any>).mockResolvedValue(null);
19
+
20
+ const result = await executeListToolProfiles(mockDb);
21
+ expect(result).toContain("[Profile ID: __builtin_default]");
22
+ expect(result).toContain("Tools:");
23
+ });
24
+
25
+ it("includes custom profiles when stored in DB as JSON string or array", async () => {
26
+ const customProfiles = [
27
+ {
28
+ id: "coding",
29
+ name: "Coding Profile",
30
+ enabledToolNames: ["bash", "read_file", "write_file"],
31
+ },
32
+ ];
33
+ (getConfig as jest.Mock<any>).mockResolvedValue(
34
+ JSON.stringify(customProfiles),
35
+ );
36
+
37
+ const result = await executeListToolProfiles(mockDb);
38
+ expect(result).toContain("[Profile ID: __builtin_default]");
39
+ expect(result).toContain("[Profile ID: coding] Coding Profile");
40
+ expect(result).toContain("Tools: bash, read_file, write_file");
41
+ });
42
+ });
@@ -0,0 +1,34 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ jest.unstable_mockModule("../../utils/post.js", () => ({
4
+ post: jest.fn(),
5
+ }));
6
+
7
+ const { executeManageTools } = await import("./manage-tools.js");
8
+ const { post } = await import("../../utils/post.js");
9
+
10
+ describe("executeManageTools", () => {
11
+ it("posts manage-tools message with action and tool details", () => {
12
+ const result = executeManageTools(
13
+ {
14
+ action: "enable",
15
+ tool_names: ["git_status", "git_commit"],
16
+ profile_id: "git-dev",
17
+ },
18
+ "group-456",
19
+ );
20
+
21
+ expect(result).toContain(
22
+ "Tool management request sent: enable git-dev (git_status, git_commit)",
23
+ );
24
+ expect(post).toHaveBeenCalledWith({
25
+ type: "manage-tools",
26
+ payload: {
27
+ action: "enable",
28
+ groupId: "group-456",
29
+ profileId: "git-dev",
30
+ toolNames: ["git_status", "git_commit"],
31
+ },
32
+ });
33
+ });
34
+ });