shadow-claw 1.23.4 → 1.24.1

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 (267) hide show
  1. package/README.md +42 -5
  2. package/bin/cli.mjs +166 -0
  3. package/bin/cli.test.mjs +60 -0
  4. package/bin/commands/backup.mjs +70 -0
  5. package/bin/commands/clients.mjs +41 -0
  6. package/bin/commands/peer-id.mjs +76 -0
  7. package/bin/commands/peer-id.test.mjs +104 -0
  8. package/bin/commands/send.mjs +51 -0
  9. package/bin/commands/tasks.mjs +59 -0
  10. package/bin/commands/webrtc-listen.mjs +254 -0
  11. package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
  12. package/bin/rolldown.config.test.mjs +401 -8
  13. package/bin/utils/control-client.mjs +243 -0
  14. package/bin/utils/control-client.test.mjs +83 -0
  15. package/bin/utils/ulid.mjs +45 -0
  16. package/bin/utils/webrtc-control-client.mjs +699 -0
  17. package/bin/utils/webrtc-control-client.test.mjs +318 -0
  18. package/dist/electron/main.cjs +1 -1
  19. package/dist/public/AGENTS.md +12 -3
  20. package/dist/public/README.md +42 -5
  21. package/dist/public/agent.worker.js +50 -50
  22. package/dist/public/backup-controller-CvzDOq4T.js +1 -0
  23. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
  24. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
  25. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
  26. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  27. package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
  28. package/dist/public/config-CrHchneq.js +1 -0
  29. package/dist/public/{configurePeerJs-B4Ym9VMj.js → configurePeerJs-UfQ6HUlv.js} +1 -1
  30. package/dist/public/{connections-BEn6JYdu.js → connections-DvbgdkaB.js} +1 -1
  31. package/dist/public/{custom-element-security-DdYlUozq.js → custom-element-security-NCqQ5lmC.js} +1 -1
  32. package/dist/public/docs/README.md +3 -1
  33. package/dist/public/docs/architecture/overview.md +12 -12
  34. package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
  35. package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +2 -2
  36. package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
  37. package/dist/public/docs/example/article/index.html +1 -1
  38. package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
  39. package/dist/public/docs/guides/server-development-configuration.md +24 -0
  40. package/dist/public/docs/publishing/index.html +1 -1
  41. package/dist/public/docs/skill-creator/index.html +1 -1
  42. package/dist/public/docs/subsystems/backup.md +56 -0
  43. package/dist/public/docs/subsystems/cli.md +124 -0
  44. package/dist/public/docs/subsystems/control-plane.md +169 -0
  45. package/dist/public/docs/subsystems/notifications.md +54 -2
  46. package/dist/public/docs/subsystems/reactive-ui.md +1 -0
  47. package/dist/public/downloadGroupFile-9niHBj9n.js +1 -0
  48. package/dist/public/e2e/README.md +5 -1
  49. package/dist/public/{e2e-bridge-BQ4KzUt5.js → e2e-bridge-D28tOggc.js} +1 -1
  50. package/dist/public/file-viewer-BVg3B_JH.js +1 -0
  51. package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
  52. package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
  53. package/dist/public/{git-DEUvQTUA.js → git-7Vq_SJSp.js} +2 -2
  54. package/dist/public/{iframe-sanitizer-0NS4XLOd.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
  55. package/dist/public/index.html +1 -1
  56. package/dist/public/index.js +1 -1
  57. package/dist/public/{initChatSplitResize-llGkSJXg.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
  58. package/dist/public/initControlPlane-DKNht5ph.js +1 -0
  59. package/dist/public/llms.txt +1 -1
  60. package/dist/public/main/index.html +1 -1
  61. package/dist/public/main/memory/index.html +1 -1
  62. package/dist/public/{markdown-CyYR4App.js → markdown-SiFq0PTy.js} +1 -1
  63. package/dist/public/{mcp-reconnect-CSx4aMwy.js → mcp-reconnect-9emsqGRe.js} +1 -1
  64. package/dist/public/{memoryStorage-Dwg8NhAZ.js → memoryStorage-C0KvLNUp.js} +1 -1
  65. package/dist/public/orchestrator-8w9-exfN.js +15 -0
  66. package/dist/public/{orchestrator-DRfEudXk.js → orchestrator-tHWrqi_F.js} +676 -676
  67. package/dist/public/{peerjs-CurBuKvk.js → peerjs-D8hck8Ih.js} +1 -1
  68. package/dist/public/push-client-D0lkwrK0.js +1 -0
  69. package/dist/public/service-worker/fetch-proxy.js +1 -1
  70. package/dist/public/service-worker/push-handler.js +1 -1
  71. package/dist/public/service-worker.js +1 -1
  72. package/dist/public/{shadow-claw-5IHaDDMJ.js → shadow-claw-Cs8wbk3d.js} +2 -2
  73. package/dist/public/{shadow-claw-accounts-CZvz4iQz.js → shadow-claw-accounts-B4sBTQEj.js} +1 -1
  74. package/dist/public/{shadow-claw-channels-BKXFyRS3.js → shadow-claw-channels-mY-JMvib.js} +1 -1
  75. package/dist/public/{shadow-claw-chat-CCLuhWdI.js → shadow-claw-chat-DGPKpmU5.js} +6 -6
  76. package/dist/public/shadow-claw-control-plane-B1F0jnPk.js +199 -0
  77. package/dist/public/{shadow-claw-conversations-DV-87UUh.js → shadow-claw-conversations-0Q-VC_7N.js} +1 -1
  78. package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
  79. package/dist/public/{shadow-claw-file-viewer-Dk2dbAUd.js → shadow-claw-file-viewer-AZfG4D-u.js} +1 -1
  80. package/dist/public/shadow-claw-files-Bx4v0Ggp.js +840 -0
  81. package/dist/public/{shadow-claw-git-DRA7_qck.js → shadow-claw-git-DD7iQRxq.js} +2 -2
  82. package/dist/public/{shadow-claw-integrations-1Xu5snuV.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
  83. package/dist/public/{shadow-claw-llm-6Aci1BDP.js → shadow-claw-llm-CfeDBZKB.js} +2 -2
  84. package/dist/public/{shadow-claw-mcp-remote-BUjAeVXC.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
  85. package/dist/public/{shadow-claw-networking-FQe4nCp3.js → shadow-claw-networking-e0EarDVs.js} +1 -1
  86. package/dist/public/{shadow-claw-notifications-DV2wibL3.js → shadow-claw-notifications-BkxuDEQI.js} +3 -3
  87. package/dist/public/{shadow-claw-page-header-B63S4HTU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
  88. package/dist/public/{shadow-claw-pages-DE3jWj5K.js → shadow-claw-pages-BWSE91I1.js} +1 -1
  89. package/dist/public/{shadow-claw-settings-S6DAKfYx.js → shadow-claw-settings-D6lPBn6m.js} +10 -3
  90. package/dist/public/{shadow-claw-storage-DaacA3_K.js → shadow-claw-storage-Crs6LY15.js} +2 -2
  91. package/dist/public/{shadow-claw-task-server-IjKX042p.js → shadow-claw-task-server-CKm_Ennl.js} +25 -4
  92. package/dist/public/{shadow-claw-tasks-Cy0JYmGU.js → shadow-claw-tasks-Czx6Ucf5.js} +1 -1
  93. package/dist/public/{shadow-claw-terminal-znAt7HyP.js → shadow-claw-terminal-Dmtr9KY1.js} +1 -1
  94. package/dist/public/{shadow-claw-tools-Cutv9Si5.js → shadow-claw-tools-t3Qq4TDO.js} +1 -1
  95. package/dist/public/{shadow-claw-webvm-BH8mPDEu.js → shadow-claw-webvm-CIuSywD_.js} +1 -1
  96. package/dist/public/{syncWebMcpRegistration-BqJxaMGR.js → syncWebMcpRegistration-DzC80wVG.js} +1 -1
  97. package/dist/server.js +286 -189
  98. package/package.json +2 -1
  99. package/src/__mocks__/jszip.cjs +15 -5
  100. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
  101. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
  102. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
  103. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
  104. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
  105. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
  106. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
  107. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
  108. package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
  109. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
  110. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
  111. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
  112. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +3 -0
  113. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.ts +43 -9
  114. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
  115. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +76 -0
  116. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
  117. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  118. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
  119. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
  120. package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
  121. package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
  122. package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
  123. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
  124. package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
  125. package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
  126. package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
  127. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
  128. package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
  129. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +273 -0
  130. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +408 -1
  131. package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
  132. package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
  133. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
  134. package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
  135. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
  136. package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
  137. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
  138. package/src/config/config.ts +10 -0
  139. package/src/core/backup-controller.test.ts +101 -0
  140. package/src/core/backup-controller.ts +166 -0
  141. package/src/core/control-plane-client.test.ts +415 -0
  142. package/src/core/control-plane-client.ts +441 -0
  143. package/src/core/orchestrator/orchestrator.ts +4 -0
  144. package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
  145. package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
  146. package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
  147. package/src/core/orchestrator/utils/operations/task.ts +46 -15
  148. package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
  149. package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
  150. package/src/core/utils/initControlPlane.test.ts +276 -0
  151. package/src/core/utils/initControlPlane.ts +437 -0
  152. package/src/core/utils/initializeApp.ts +18 -0
  153. package/src/db/getSession.test.ts +13 -1
  154. package/src/server/app.test.ts +12 -0
  155. package/src/server/app.ts +20 -8
  156. package/src/server/client-registry.test.ts +250 -0
  157. package/src/server/client-registry.ts +477 -0
  158. package/src/server/config.ts +15 -0
  159. package/src/server/control-plane-types.ts +120 -0
  160. package/src/server/control-plane.test.ts +508 -0
  161. package/src/server/control-plane.ts +607 -0
  162. package/src/server/middleware/pna.test.ts +15 -3
  163. package/src/server/middleware/pna.ts +6 -2
  164. package/src/server/routes/backup.test.ts +277 -0
  165. package/src/server/routes/backup.ts +291 -0
  166. package/src/server/routes/integrations-email.test.ts +532 -0
  167. package/src/server/routes/llamafile.test.ts +172 -48
  168. package/src/server/routes/mesh-llm.test.ts +192 -0
  169. package/src/server/routes/ollama.test.ts +49 -0
  170. package/src/server/server-peer.test.ts +86 -0
  171. package/src/server/server-peer.ts +330 -0
  172. package/src/server/server.ts +37 -0
  173. package/src/server/services/llamafile-manager.test.ts +230 -6
  174. package/src/server/services/transformers-runtime.test.ts +186 -18
  175. package/src/server/webrtc.test.ts +21 -0
  176. package/src/server/webrtc.ts +72 -0
  177. package/src/service-worker/fetch-proxy-rules.test.ts +41 -0
  178. package/src/service-worker/fetch-proxy-rules.ts +15 -8
  179. package/src/service-worker/fetch-proxy.test.ts +64 -1
  180. package/src/service-worker/fetch-proxy.ts +18 -2
  181. package/src/service-worker/push-handler.ts +39 -0
  182. package/src/shell/vm.test.ts +744 -12
  183. package/src/storage/ensureMainGroupIndex.test.ts +153 -0
  184. package/src/storage/getGroupFile.test.ts +66 -0
  185. package/src/storage/memoryStorage.test.ts +82 -2
  186. package/src/storage/writeGroupFileBytes.test.ts +143 -0
  187. package/src/stores/chat-ui.test.ts +20 -0
  188. package/src/stores/orchestrator.test.ts +134 -0
  189. package/src/stores/toast.test.ts +50 -0
  190. package/src/stores/tools.test.ts +147 -10
  191. package/src/subsystems/channels/imessage.test.ts +59 -0
  192. package/src/subsystems/channels/peerjs.test.ts +500 -62
  193. package/src/subsystems/channels/peerjs.ts +71 -4
  194. package/src/subsystems/channels/telegram.test.ts +177 -0
  195. package/src/subsystems/email/catalog.test.ts +1 -0
  196. package/src/subsystems/email/connections.test.ts +39 -1
  197. package/src/subsystems/git/credentials.test.ts +160 -825
  198. package/src/subsystems/git/git.test.ts +463 -0
  199. package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
  200. package/src/subsystems/notifications/push-client.test.ts +5 -1
  201. package/src/subsystems/notifications/push-client.ts +50 -12
  202. package/src/subsystems/notifications/push-routes.test.ts +33 -0
  203. package/src/subsystems/notifications/push-routes.ts +23 -0
  204. package/src/subsystems/notifications/push-store.ts +1 -1
  205. package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
  206. package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
  207. package/src/subsystems/providers/model-registry.test.ts +115 -0
  208. package/src/subsystems/providers/providers.test.ts +94 -1
  209. package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
  210. package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
  211. package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
  212. package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
  213. package/src/subsystems/skills/activateSkill.test.ts +101 -0
  214. package/src/subsystems/skills/discoverSkills.test.ts +251 -0
  215. package/src/subsystems/tools/cron.test.ts +64 -0
  216. package/src/subsystems/tools/declarative.test.ts +255 -89
  217. package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
  218. package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
  219. package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
  220. package/src/testing/jest-setup.ts +328 -40
  221. package/src/testing/mock-v86.mjs +49 -0
  222. package/src/ui/a2ui/types.ts +4 -3
  223. package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
  224. package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
  225. package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
  226. package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
  227. package/src/worker/tools/email/email.test.ts +193 -8
  228. package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
  229. package/src/worker/tools/git/git.test.ts +214 -3
  230. package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
  231. package/src/worker/tools/rooms/leave-room.test.ts +40 -0
  232. package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
  233. package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
  234. package/src/worker/tools/ui/ask-user.test.ts +50 -0
  235. package/src/worker/tools/ui/clear-chat.test.ts +19 -0
  236. package/src/worker/tools/ui/get-current-time.test.ts +19 -0
  237. package/src/worker/tools/ui/javascript.test.ts +116 -0
  238. package/src/worker/tools/ui/list-components.test.ts +10 -0
  239. package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
  240. package/src/worker/tools/ui/manage-tools.test.ts +34 -0
  241. package/src/worker/tools/ui/render-component.test.ts +346 -0
  242. package/src/worker/tools/ui/send-notification.test.ts +40 -0
  243. package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
  244. package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
  245. package/src/worker/tools/workspace/attach-file.test.ts +84 -0
  246. package/src/worker/tools/workspace/copy-file.test.ts +69 -0
  247. package/src/worker/tools/workspace/create-directory.test.ts +40 -0
  248. package/src/worker/tools/workspace/delete-file.test.ts +73 -0
  249. package/src/worker/tools/workspace/diff-files.test.ts +94 -0
  250. package/src/worker/tools/workspace/list-files.test.ts +36 -0
  251. package/src/worker/tools/workspace/move-file.test.ts +66 -0
  252. package/src/worker/tools/workspace/open-file.test.ts +57 -0
  253. package/src/worker/tools/workspace/patch-file.test.ts +81 -0
  254. package/src/worker/tools/workspace/read-file.test.ts +116 -0
  255. package/src/worker/tools/workspace/send-file.test.ts +82 -0
  256. package/src/worker/tools/workspace/update-memory.test.ts +29 -0
  257. package/src/worker/tools/workspace/write-file.test.ts +28 -0
  258. package/src/worker/utils/rate-limit.test.ts +199 -0
  259. package/src/worker/utils/tool-state.test.ts +29 -0
  260. package/src/worker/utils/transformers-js-utils.test.ts +59 -0
  261. package/dist/public/config-BYAuxx3j.js +0 -1
  262. package/dist/public/downloadGroupFile-Drdvg1Ev.js +0 -1
  263. package/dist/public/file-viewer-CcZYw-PS.js +0 -1
  264. package/dist/public/orchestrator-o9Fj67BN.js +0 -15
  265. package/dist/public/push-client-Dii-vPEh.js +0 -1
  266. package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
  267. package/dist/public/shadow-claw-files-BpfpJIuP.js +0 -840
@@ -5,6 +5,7 @@ import {
5
5
  formatRequest,
6
6
  getAdapter,
7
7
  getContextLimit,
8
+ normalizeMeshLlmResult,
8
9
  parseResponse,
9
10
  } from "./providers.js";
10
11
 
@@ -64,10 +65,47 @@ describe("providers.js", () => {
64
65
  it("should return correct limit for Claude 3 models", () => {
65
66
  expect(getContextLimit("claude-3-opus")).toBe(200000);
66
67
  expect(getContextLimit("claude-3-haiku")).toBe(200000);
68
+ expect(getContextLimit("claude-opus-4")).toBe(1000000);
67
69
  });
68
70
 
69
- it("should return correct limit for GPT-4", () => {
71
+ it("should return correct limit for GPT-4 and reasoning models", () => {
70
72
  expect(getContextLimit("gpt-4")).toBe(8192);
73
+ expect(getContextLimit("o1-preview")).toBe(200000);
74
+ expect(getContextLimit("o3-mini")).toBe(200000);
75
+ });
76
+
77
+ it("should return correct limit for Gemini models", () => {
78
+ expect(getContextLimit("gemini-1.5-pro")).toBe(2097152);
79
+ expect(getContextLimit("gemini-2.0-flash")).toBe(1048576);
80
+ expect(getContextLimit("gemini-1.0-pro")).toBe(32768);
81
+ expect(getContextLimit("gemini-nano")).toBe(4096);
82
+ });
83
+
84
+ it("should return correct limit for Llama, Gemma, and Mistral models", () => {
85
+ expect(getContextLimit("llama-3.1-70b")).toBe(128000);
86
+ expect(getContextLimit("llama-3-8b")).toBe(8192);
87
+ expect(getContextLimit("gemma-4-12b")).toBe(256000);
88
+ expect(getContextLimit("gemma-4-e2b")).toBe(128000);
89
+ expect(getContextLimit("gemma-3-12b")).toBe(128000);
90
+ expect(getContextLimit("gemma-3-1b")).toBe(32000);
91
+ expect(getContextLimit("gemma-2-9b")).toBe(8192);
92
+ expect(getContextLimit("mistral-large")).toBe(128000);
93
+ expect(getContextLimit("mistral-small")).toBe(32000);
94
+ });
95
+
96
+ it("should return correct limit for Bedrock, Phi, Qwen, and Mesh models", () => {
97
+ expect(getContextLimit("amazon.nova-micro")).toBe(128000);
98
+ expect(getContextLimit("amazon.nova-pro")).toBe(300000);
99
+ expect(getContextLimit("amazon.titan-text")).toBe(8192);
100
+ expect(getContextLimit("cohere.command-r")).toBe(128000);
101
+ expect(getContextLimit("phi-4-mini")).toBe(128000);
102
+ expect(getContextLimit("phi-4")).toBe(16384);
103
+ expect(getContextLimit("phi-3.5")).toBe(128000);
104
+ expect(getContextLimit("phi-3")).toBe(4096);
105
+ expect(getContextLimit("qwen3.5-9b")).toBe(262144);
106
+ expect(getContextLimit("qwen3-0.6b")).toBe(40960);
107
+ expect(getContextLimit("qwen3-8b")).toBe(128000);
108
+ expect(getContextLimit("mesh")).toBe(242144);
71
109
  });
72
110
 
73
111
  it("should return default limit for unknown models", () => {
@@ -1157,4 +1195,59 @@ describe("providers.js", () => {
1157
1195
  expect(headers["X-Token"]).toBe("Token abc123");
1158
1196
  });
1159
1197
  });
1198
+
1199
+ describe("normalizeMeshLlmResult & MeshLlmAdapter", () => {
1200
+ it("returns unchanged for empty or invalid results", () => {
1201
+ expect(normalizeMeshLlmResult(null)).toBeNull();
1202
+ expect(normalizeMeshLlmResult(undefined)).toBeUndefined();
1203
+ expect(normalizeMeshLlmResult({ content: "not-an-array" })).toEqual({
1204
+ content: "not-an-array",
1205
+ });
1206
+ });
1207
+
1208
+ it("normalizes mesh-llm result stripping thought tags and parsing tool calls", () => {
1209
+ const result = {
1210
+ content: [
1211
+ {
1212
+ type: "text",
1213
+ text: '<|channel>Thinking deeply...<channel|>Hello user!<|tool_call>call:search{"q":"test"}<tool_call|>',
1214
+ },
1215
+ { type: "image", data: "123" },
1216
+ ],
1217
+ stop_reason: "end_turn",
1218
+ };
1219
+
1220
+ const normalized = normalizeMeshLlmResult(result);
1221
+ expect(normalized.content).toEqual([
1222
+ { type: "text", text: "Hello user!" },
1223
+ expect.objectContaining({
1224
+ type: "tool_use",
1225
+ name: "search",
1226
+ input: { q: "test" },
1227
+ }),
1228
+ { type: "image", data: "123" },
1229
+ ]);
1230
+ expect(normalized.stop_reason).toBe("tool_use");
1231
+ });
1232
+
1233
+ it("returns MeshLlmAdapter for mesh-llm provider format", () => {
1234
+ const provider: any = { format: "mesh-llm" };
1235
+ const adapter = getAdapter(provider);
1236
+ expect(adapter.constructor.name).toBe("MeshLlmAdapter");
1237
+
1238
+ const response = {
1239
+ choices: [
1240
+ {
1241
+ message: {
1242
+ content: "<|channel>reasoning<channel|>Result text",
1243
+ },
1244
+ finish_reason: "stop",
1245
+ },
1246
+ ],
1247
+ };
1248
+
1249
+ const parsed = parseResponse(provider, response);
1250
+ expect(parsed.content).toEqual([{ type: "text", text: "Result text" }]);
1251
+ });
1252
+ });
1160
1253
  });
@@ -0,0 +1,279 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ describe("Transformers.js Provider", () => {
4
+ let mockWorkerInstance: any;
5
+ let mockGetConfig: jest.Mock<any>;
6
+ let mockExecuteTool: jest.Mock<any>;
7
+ let mockSetPostHandler: jest.Mock<any>;
8
+
9
+ beforeEach(() => {
10
+ jest.resetModules();
11
+
12
+ mockGetConfig = jest.fn().mockResolvedValue("auto" as never);
13
+ mockExecuteTool = jest
14
+ .fn()
15
+ .mockResolvedValue("tool success result" as never);
16
+ mockSetPostHandler = jest.fn();
17
+
18
+ mockWorkerInstance = {
19
+ postMessage: jest.fn((msg: any) => {
20
+ const { type, payload } = msg;
21
+ if (type === "load") {
22
+ setTimeout(() => {
23
+ mockWorkerInstance.onmessage?.({
24
+ data: {
25
+ type: "progress",
26
+ payload: {
27
+ groupId: payload.groupId,
28
+ status: "progress",
29
+ loaded: 50,
30
+ total: 100,
31
+ },
32
+ },
33
+ });
34
+ mockWorkerInstance.onmessage?.({
35
+ data: {
36
+ type: "progress",
37
+ payload: { groupId: payload.groupId, status: "done" },
38
+ },
39
+ });
40
+ }, 0);
41
+ } else if (type === "generate") {
42
+ setTimeout(() => {
43
+ mockWorkerInstance.onmessage?.({
44
+ data: {
45
+ type: "chunk",
46
+ payload: { groupId: payload.groupId, text: "Hello " },
47
+ },
48
+ });
49
+ mockWorkerInstance.onmessage?.({
50
+ data: {
51
+ type: "thinking-chunk",
52
+ payload: {
53
+ groupId: payload.groupId,
54
+ text: "thinking deeply...",
55
+ },
56
+ },
57
+ });
58
+ mockWorkerInstance.onmessage?.({
59
+ data: {
60
+ type: "done",
61
+ payload: {
62
+ groupId: payload.groupId,
63
+ text: "Hello world response",
64
+ },
65
+ },
66
+ });
67
+ }, 0);
68
+ }
69
+ }),
70
+ onmessage: null,
71
+ };
72
+
73
+ (globalThis as any).Worker = jest
74
+ .fn()
75
+ .mockImplementation(() => mockWorkerInstance);
76
+
77
+ jest.unstable_mockModule("../../db/getConfig.js", () => ({
78
+ getConfig: mockGetConfig,
79
+ }));
80
+
81
+ jest.unstable_mockModule("../../worker/utils/executeTool.js", () => ({
82
+ executeTool: mockExecuteTool,
83
+ }));
84
+
85
+ jest.unstable_mockModule("../../worker/utils/post.js", () => ({
86
+ post: jest.fn(),
87
+ setPostHandler: mockSetPostHandler,
88
+ }));
89
+ });
90
+
91
+ afterEach(() => {
92
+ jest.clearAllMocks();
93
+ });
94
+
95
+ it("loads model, streams chunks and completes simple response", async () => {
96
+ const { invokeWithTransformersJs } =
97
+ await import("./transformers-js-provider.js");
98
+
99
+ const emitted: any[] = [];
100
+ const emit = (msg: any) => {
101
+ emitted.push(msg);
102
+ };
103
+
104
+ const messages = [
105
+ {
106
+ role: "user",
107
+ content: [
108
+ { type: "text", text: "Hello" },
109
+ {
110
+ type: "attachment",
111
+ mediaType: "image",
112
+ fileName: "pic.png",
113
+ mimeType: "image/png",
114
+ },
115
+ ],
116
+ },
117
+ ];
118
+
119
+ await invokeWithTransformersJs(
120
+ {} as any,
121
+ "g1",
122
+ "You are a helpful assistant.",
123
+ messages,
124
+ 2048,
125
+ emit,
126
+ undefined,
127
+ [],
128
+ "Xenova/Qwen1.5-0.5B-Chat",
129
+ );
130
+
131
+ expect(emitted.some((e) => e.type === "model-download-progress")).toBe(
132
+ true,
133
+ );
134
+ expect(emitted.some((e) => e.type === "streaming-start")).toBe(true);
135
+ expect(
136
+ emitted.some(
137
+ (e) => e.type === "streaming-chunk" && e.payload.text === "Hello ",
138
+ ),
139
+ ).toBe(true);
140
+ expect(emitted.some((e) => e.type === "streaming-done")).toBe(true);
141
+ expect(
142
+ emitted.some(
143
+ (e) =>
144
+ e.type === "response" && e.payload.text === "Hello world response",
145
+ ),
146
+ ).toBe(true);
147
+ });
148
+
149
+ it("handles XML <tool_call> output, executes tool, and iterates loop", async () => {
150
+ let callCount = 0;
151
+ mockWorkerInstance.postMessage = jest.fn((msg: any) => {
152
+ const { type, payload } = msg;
153
+ if (type === "load") {
154
+ setTimeout(() => {
155
+ mockWorkerInstance.onmessage?.({
156
+ data: {
157
+ type: "progress",
158
+ payload: { groupId: payload.groupId, status: "done" },
159
+ },
160
+ });
161
+ }, 0);
162
+ } else if (type === "generate") {
163
+ callCount++;
164
+ setTimeout(() => {
165
+ if (callCount === 1) {
166
+ mockWorkerInstance.onmessage?.({
167
+ data: {
168
+ type: "done",
169
+ payload: {
170
+ groupId: payload.groupId,
171
+ text: '<tool_call>{"name": "web_search", "arguments": {"query": "weather"}}</tool_call>',
172
+ },
173
+ },
174
+ });
175
+ } else {
176
+ mockWorkerInstance.onmessage?.({
177
+ data: {
178
+ type: "done",
179
+ payload: {
180
+ groupId: payload.groupId,
181
+ text: '{"type":"response","response":"The weather is sunny."}',
182
+ },
183
+ },
184
+ });
185
+ }
186
+ }, 0);
187
+ }
188
+ });
189
+
190
+ const { invokeWithTransformersJs } =
191
+ await import("./transformers-js-provider.js");
192
+
193
+ const emitted: any[] = [];
194
+ const emit = (msg: any) => {
195
+ emitted.push(msg);
196
+ };
197
+
198
+ const tools: any[] = [
199
+ {
200
+ name: "web_search",
201
+ description: "Search web",
202
+ input_schema: { type: "object", properties: {} },
203
+ },
204
+ ];
205
+
206
+ await invokeWithTransformersJs(
207
+ {} as any,
208
+ "g2",
209
+ "System prompt",
210
+ [{ role: "user", content: "What is the weather?" }],
211
+ 1024,
212
+ emit,
213
+ undefined,
214
+ tools,
215
+ "model-id",
216
+ );
217
+
218
+ expect(mockExecuteTool).toHaveBeenCalledWith(
219
+ {},
220
+ "web_search",
221
+ { query: "weather" },
222
+ "g2",
223
+ expect.objectContaining({ allowedTools: tools }),
224
+ );
225
+ expect(
226
+ emitted.some(
227
+ (e) =>
228
+ e.type === "response" && e.payload.text === "The weather is sunny.",
229
+ ),
230
+ ).toBe(true);
231
+ });
232
+
233
+ it("handles worker error rejection during generation", async () => {
234
+ mockWorkerInstance.postMessage = jest.fn((msg: any) => {
235
+ const { type, payload } = msg;
236
+ if (type === "load") {
237
+ setTimeout(() => {
238
+ mockWorkerInstance.onmessage?.({
239
+ data: {
240
+ type: "progress",
241
+ payload: { groupId: payload.groupId, status: "done" },
242
+ },
243
+ });
244
+ }, 0);
245
+ } else if (type === "generate") {
246
+ setTimeout(() => {
247
+ mockWorkerInstance.onmessage?.({
248
+ data: {
249
+ type: "error",
250
+ payload: { groupId: payload.groupId, error: "Out of memory" },
251
+ },
252
+ });
253
+ }, 0);
254
+ }
255
+ });
256
+
257
+ const { invokeWithTransformersJs } =
258
+ await import("./transformers-js-provider.js");
259
+
260
+ const emitted: any[] = [];
261
+ const emit = (msg: any) => {
262
+ emitted.push(msg);
263
+ };
264
+
265
+ await expect(
266
+ invokeWithTransformersJs(
267
+ {} as any,
268
+ "g3",
269
+ "System prompt",
270
+ [{ role: "user", content: "Hi" }],
271
+ 512,
272
+ emit,
273
+ undefined,
274
+ [],
275
+ "model-id",
276
+ ),
277
+ ).rejects.toBe("Out of memory");
278
+ });
279
+ });
@@ -2,8 +2,8 @@ import { jest } from "@jest/globals";
2
2
  import { createModelCacheFetch } from "./createModelCacheFetch.js";
3
3
 
4
4
  // Mock Blob and Response for Jest/jsdom
5
- if (typeof global !== "undefined") {
6
- (global as any).Blob = class MockBlob {
5
+ if (typeof globalThis !== "undefined") {
6
+ (globalThis as any).Blob = class MockBlob {
7
7
  private parts: any[];
8
8
  public size: number;
9
9
  constructor(parts: any[]) {
@@ -2,8 +2,8 @@ import { jest } from "@jest/globals";
2
2
  import { downloadModelToCache } from "./downloadModelToCache.js";
3
3
 
4
4
  // jsdom does not implement Blob fully, so we mock it for the tests
5
- if (typeof global !== "undefined") {
6
- (global as any).Blob = class MockBlob {
5
+ if (typeof globalThis !== "undefined") {
6
+ (globalThis as any).Blob = class MockBlob {
7
7
  private parts: any[];
8
8
  public size: number;
9
9
  constructor(parts: any[]) {
@@ -2,8 +2,8 @@ import { jest } from "@jest/globals";
2
2
  import { loadModelStream } from "./loadModelStream.js";
3
3
 
4
4
  // Mock Blob and Response for Jest/jsdom
5
- if (typeof global !== "undefined") {
6
- (global as any).Blob = class MockBlob {
5
+ if (typeof globalThis !== "undefined") {
6
+ (globalThis as any).Blob = class MockBlob {
7
7
  private parts: any[];
8
8
  public size: number;
9
9
  constructor(parts: any[]) {
@@ -0,0 +1,101 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ const mockLoadSkill = jest.fn() as any;
4
+
5
+ jest.unstable_mockModule("./discoverSkills.js", () => ({
6
+ loadSkill: mockLoadSkill,
7
+ }));
8
+
9
+ const { executeActivateSkill } = await import("./activateSkill.js");
10
+
11
+ describe("executeActivateSkill", () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ });
15
+
16
+ it("returns error when skill name is missing or empty", async () => {
17
+ const res1 = await executeActivateSkill({} as any, {}, "group-1");
18
+ expect(res1).toBe("Error: activate_skill requires a skill name.");
19
+
20
+ const res2 = await executeActivateSkill(
21
+ {} as any,
22
+ { name: " " },
23
+ "group-1",
24
+ );
25
+ expect(res2).toBe("Error: activate_skill requires a skill name.");
26
+ });
27
+
28
+ it("returns error when skill is not found", async () => {
29
+ mockLoadSkill.mockResolvedValue(null);
30
+
31
+ const res = await executeActivateSkill(
32
+ {} as any,
33
+ { name: "my-skill" },
34
+ "group-1",
35
+ );
36
+ expect(res).toBe(
37
+ 'Error: skill "my-skill" was not found in this conversation workspace.',
38
+ );
39
+ });
40
+
41
+ it("returns error when skill disables model invocation", async () => {
42
+ mockLoadSkill.mockResolvedValue({
43
+ name: "secret-skill",
44
+ disableModelInvocation: true,
45
+ });
46
+
47
+ const res = await executeActivateSkill(
48
+ {} as any,
49
+ { name: "secret-skill" },
50
+ "group-1",
51
+ );
52
+ expect(res).toBe(
53
+ 'Error: skill "secret-skill" is not available for model-driven activation.',
54
+ );
55
+ });
56
+
57
+ it("formats skill content without resources", async () => {
58
+ mockLoadSkill.mockResolvedValue({
59
+ name: "summarize",
60
+ body: "Instructions for summarizing texts.",
61
+ basePath: ".agents/skills/summarize",
62
+ resources: [],
63
+ });
64
+
65
+ const res = await executeActivateSkill(
66
+ {} as any,
67
+ { name: "summarize" },
68
+ "group-1",
69
+ );
70
+ expect(res).toContain('<skill_content name="summarize">');
71
+ expect(res).toContain("Instructions for summarizing texts.");
72
+ expect(res).toContain("Skill directory: .agents/skills/summarize");
73
+ expect(res).not.toContain("<skill_resources>");
74
+ expect(res).toContain("</skill_content>");
75
+ });
76
+
77
+ it("formats skill content with fallback body and resources", async () => {
78
+ mockLoadSkill.mockResolvedValue({
79
+ name: "data-analysis",
80
+ body: "",
81
+ basePath: ".agents/skills/data-analysis",
82
+ resources: [
83
+ ".agents/skills/data-analysis/template.py",
84
+ ".agents/skills/data-analysis/data.csv",
85
+ ],
86
+ });
87
+
88
+ const res = await executeActivateSkill(
89
+ {} as any,
90
+ { name: "data-analysis" },
91
+ "group-1",
92
+ );
93
+ expect(res).toContain("(This skill has no instructions.)");
94
+ expect(res).toContain("<skill_resources>");
95
+ expect(res).toContain(
96
+ "<file>.agents/skills/data-analysis/template.py</file>",
97
+ );
98
+ expect(res).toContain("<file>.agents/skills/data-analysis/data.csv</file>");
99
+ expect(res).toContain("</skill_resources>");
100
+ });
101
+ });