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
@@ -98,6 +98,10 @@ const mockSetMaxTokens = jest.fn(async () => {});
98
98
  const mockSetRateLimitCallsPerMinute = jest.fn(async () => {});
99
99
  const mockSetRateLimitAutoAdapt = jest.fn(async () => {});
100
100
  const mockSetReasoningEffort = jest.fn(async () => {});
101
+ const mockSetMaxIterations = jest.fn(async () => {});
102
+ const mockSetLlamafileSettings = jest.fn(async () => {});
103
+ const mockSetContextCompressionEnabled = jest.fn(async () => {});
104
+ const mockSetStreamingEnabled = jest.fn(async () => {});
101
105
 
102
106
  jest.unstable_mockModule(
103
107
  "../../../core/orchestrator/utils/operations/provider.js",
@@ -128,7 +132,7 @@ jest.unstable_mockModule(
128
132
  getProviderRuntimeHeaders: jest.fn().mockReturnValue({}),
129
133
  setProvider: mockSetProvider,
130
134
  setBedrockSettings: mockSetBedrockSettings,
131
- setLlamafileSettings: jest.fn(async () => {}),
135
+ setLlamafileSettings: mockSetLlamafileSettings,
132
136
  setMeshLlmSettings: jest.fn(async () => {}),
133
137
  setModel: jest.fn(async () => {}),
134
138
  setAssistantName: jest.fn(async () => {}),
@@ -151,11 +155,11 @@ jest.unstable_mockModule(
151
155
  setGitProxyUrl: jest.fn(async () => {}),
152
156
  setProxyUrl: jest.fn(async () => {}),
153
157
  setVmInternetAccess: jest.fn(async () => {}),
154
- setStreamingEnabled: jest.fn(async () => {}),
158
+ setStreamingEnabled: mockSetStreamingEnabled,
155
159
  setUseProxy: jest.fn(async () => {}),
156
160
  setVMBashFullInternetAccess: jest.fn(async () => {}),
157
- setContextCompressionEnabled: jest.fn(async () => {}),
158
- setMaxIterations: jest.fn(async () => {}),
161
+ setContextCompressionEnabled: mockSetContextCompressionEnabled,
162
+ setMaxIterations: mockSetMaxIterations,
159
163
  }),
160
164
  );
161
165
 
@@ -196,6 +200,7 @@ const { setConfig } = await import("../../../db/setConfig.js");
196
200
 
197
201
  function createOrchestratorStub(overrides = {}) {
198
202
  const base = {
203
+ setApiKey: jest.fn<any>().mockResolvedValue(undefined),
199
204
  setProvider: jest.fn<any>().mockResolvedValue(undefined),
200
205
  setModel: jest.fn<any>().mockResolvedValue(undefined),
201
206
  setAssistantName: jest.fn<any>().mockResolvedValue(undefined),
@@ -819,4 +824,302 @@ describe("shadow-claw-llm", () => {
819
824
 
820
825
  document.body.removeChild(el);
821
826
  });
827
+
828
+ it("saves subagent max parallel, workspace mode, and reasoning effort", async () => {
829
+ const el = new ShadowClawLlm() as any;
830
+ el.db = {};
831
+ el.orchestrator = createOrchestratorStub();
832
+ document.body.appendChild(el);
833
+ await el.render();
834
+
835
+ const maxParallelInput = el.shadowRoot?.querySelector(
836
+ '[data-setting="subagent-max-parallel-input"]',
837
+ ) as HTMLInputElement | null;
838
+ if (maxParallelInput) {
839
+ maxParallelInput.value = "8";
840
+ }
841
+ await el.saveSubagentMaxParallel();
842
+ expect(setConfig).toHaveBeenCalledWith(
843
+ expect.anything(),
844
+ "subagent_max_parallel",
845
+ "8",
846
+ );
847
+
848
+ const wsModeSelect = el.shadowRoot?.querySelector(
849
+ '[data-setting="subagent-workspace-mode-select"]',
850
+ ) as HTMLSelectElement | null;
851
+ if (wsModeSelect) {
852
+ wsModeSelect.value = "isolated";
853
+ }
854
+ await el.saveSubagentWorkspaceMode();
855
+ expect(setConfig).toHaveBeenCalledWith(
856
+ expect.anything(),
857
+ "subagent_workspace_mode",
858
+ "isolated",
859
+ );
860
+
861
+ const reasoningSelect = el.shadowRoot?.querySelector(
862
+ '[data-setting="reasoning-effort-select"]',
863
+ ) as HTMLSelectElement | null;
864
+ if (reasoningSelect) {
865
+ reasoningSelect.value = "high";
866
+ }
867
+ await el.saveReasoningEffort();
868
+ expect(mockSetReasoningEffort).toHaveBeenCalledWith(
869
+ expect.anything(),
870
+ expect.anything(),
871
+ "high",
872
+ );
873
+
874
+ document.body.removeChild(el);
875
+ });
876
+
877
+ it("saves max iterations and context compression settings", async () => {
878
+ const el = new ShadowClawLlm() as any;
879
+ el.db = {};
880
+ const orch = createOrchestratorStub();
881
+ (orchestratorStore as any).orchestrator = orch;
882
+ document.body.appendChild(el);
883
+ await el.render();
884
+
885
+ const maxIterInput = el.shadowRoot?.querySelector(
886
+ '[data-setting="max-iterations-input"]',
887
+ ) as HTMLInputElement | null;
888
+ if (maxIterInput) {
889
+ maxIterInput.value = "100";
890
+ }
891
+ await el.saveMaxIterations();
892
+ expect(mockSetMaxIterations).toHaveBeenCalledWith(
893
+ expect.anything(),
894
+ expect.anything(),
895
+ 100,
896
+ );
897
+
898
+ await el.onContextCompressionToggle(true);
899
+ expect(mockSetContextCompressionEnabled).toHaveBeenCalledWith(
900
+ expect.anything(),
901
+ expect.anything(),
902
+ true,
903
+ );
904
+
905
+ document.body.removeChild(el);
906
+ });
907
+
908
+ it("saves rate limit settings and applies recommended max tokens", async () => {
909
+ const el = new ShadowClawLlm() as any;
910
+ el.db = {};
911
+ const orch = createOrchestratorStub();
912
+ (orchestratorStore as any).orchestrator = orch;
913
+ document.body.appendChild(el);
914
+ await el.render();
915
+
916
+ const rateInput = el.shadowRoot?.querySelector(
917
+ '[data-setting="rate-limit-calls-per-minute-input"]',
918
+ ) as HTMLInputElement | null;
919
+ const adaptInput = el.shadowRoot?.querySelector(
920
+ '[data-setting="rate-limit-auto-adapt-toggle"]',
921
+ ) as HTMLInputElement | null;
922
+
923
+ if (rateInput) rateInput.value = "30";
924
+ if (adaptInput) adaptInput.checked = true;
925
+
926
+ await el.saveRateLimitSettings();
927
+ expect(mockSetRateLimitCallsPerMinute).toHaveBeenCalledWith(
928
+ expect.anything(),
929
+ expect.anything(),
930
+ 30,
931
+ );
932
+ expect(mockSetRateLimitAutoAdapt).toHaveBeenCalledWith(
933
+ expect.anything(),
934
+ expect.anything(),
935
+ true,
936
+ );
937
+
938
+ const maxTokensInput = el.shadowRoot?.querySelector(
939
+ '[data-setting="max-tokens-input"]',
940
+ ) as HTMLInputElement | null;
941
+ await el.applyRecommendedMaxTokens();
942
+ expect(maxTokensInput?.value).toBeTruthy();
943
+
944
+ document.body.removeChild(el);
945
+ });
946
+
947
+ it("saves bedrock and llamafile settings", async () => {
948
+ const el = new ShadowClawLlm() as any;
949
+ el.db = {};
950
+ const orch = createOrchestratorStub();
951
+ (orchestratorStore as any).orchestrator = orch;
952
+ document.body.appendChild(el);
953
+ await el.render();
954
+
955
+ const regionInput = el.shadowRoot?.querySelector(
956
+ '[data-setting="bedrock-region-input"]',
957
+ ) as HTMLInputElement | null;
958
+ const profileInput = el.shadowRoot?.querySelector(
959
+ '[data-setting="bedrock-profile-input"]',
960
+ ) as HTMLInputElement | null;
961
+
962
+ if (regionInput) regionInput.value = "us-west-2";
963
+ if (profileInput) profileInput.value = "default";
964
+
965
+ await el.saveBedrockSettings();
966
+ expect(mockSetBedrockSettings).toHaveBeenCalledWith(
967
+ expect.anything(),
968
+ expect.anything(),
969
+ expect.objectContaining({ region: "us-west-2", profile: "default" }),
970
+ );
971
+
972
+ const modeInput = el.shadowRoot?.querySelector(
973
+ '[data-setting="llamafile-mode-select"]',
974
+ ) as HTMLSelectElement | null;
975
+ const hostInput = el.shadowRoot?.querySelector(
976
+ '[data-setting="llamafile-host-input"]',
977
+ ) as HTMLInputElement | null;
978
+ const portInput = el.shadowRoot?.querySelector(
979
+ '[data-setting="llamafile-port-input"]',
980
+ ) as HTMLInputElement | null;
981
+ const offlineInput = el.shadowRoot?.querySelector(
982
+ '[data-setting="llamafile-offline-toggle"]',
983
+ ) as HTMLInputElement | null;
984
+
985
+ if (modeInput) modeInput.value = "server";
986
+ if (hostInput) hostInput.value = "http://127.0.0.1";
987
+ if (portInput) portInput.value = "8080";
988
+ if (offlineInput) offlineInput.checked = true;
989
+
990
+ await el.saveLlamafileSettings();
991
+ expect(mockSetLlamafileSettings).toHaveBeenCalledWith(
992
+ expect.anything(),
993
+ expect.anything(),
994
+ expect.objectContaining({
995
+ mode: "server",
996
+ host: "http://127.0.0.1",
997
+ port: 8080,
998
+ offline: true,
999
+ }),
1000
+ );
1001
+
1002
+ document.body.removeChild(el);
1003
+ });
1004
+
1005
+ it("handles saveReasoningEffort, saveSubagentMaxParallel, and onStreamingToggle", async () => {
1006
+ const orch = createOrchestratorStub();
1007
+ (orchestratorStore as any).orchestrator = orch;
1008
+
1009
+ const el = new ShadowClawLlm() as any;
1010
+ document.body.appendChild(el);
1011
+ await el.render();
1012
+
1013
+ // 1. saveReasoningEffort
1014
+ const select = el.shadowRoot?.querySelector(
1015
+ '[data-setting="reasoning-effort-select"]',
1016
+ ) as HTMLSelectElement | null;
1017
+ if (select) select.value = "high";
1018
+ await el.saveReasoningEffort();
1019
+ expect(mockSetReasoningEffort).toHaveBeenCalledWith(
1020
+ orch,
1021
+ expect.anything(),
1022
+ "high",
1023
+ );
1024
+
1025
+ // 2. saveSubagentMaxParallel
1026
+ const parallelInput = el.shadowRoot?.querySelector(
1027
+ '[data-setting="subagent-max-parallel-input"]',
1028
+ ) as HTMLInputElement | null;
1029
+ if (parallelInput) parallelInput.value = "5";
1030
+ await el.saveSubagentMaxParallel();
1031
+ expect(setConfig).toHaveBeenCalledWith(
1032
+ expect.anything(),
1033
+ "subagent_max_parallel",
1034
+ "5",
1035
+ );
1036
+
1037
+ // 3. onStreamingToggle
1038
+ await el.onStreamingToggle(true);
1039
+ expect(mockSetStreamingEnabled).toHaveBeenCalled();
1040
+
1041
+ // 4. saveMaxIterations
1042
+ const maxIterInput = el.shadowRoot?.querySelector(
1043
+ '[data-setting="max-iterations-input"]',
1044
+ ) as HTMLInputElement | null;
1045
+ if (maxIterInput) maxIterInput.value = "42";
1046
+ await el.saveMaxIterations();
1047
+ expect(mockSetMaxIterations).toHaveBeenCalledWith(
1048
+ orch,
1049
+ expect.anything(),
1050
+ 42,
1051
+ );
1052
+
1053
+ // 5. saveMaxTokens
1054
+ const maxTokensInput = el.shadowRoot?.querySelector(
1055
+ '[data-setting="max-tokens-input"]',
1056
+ ) as HTMLInputElement | null;
1057
+ if (maxTokensInput) maxTokensInput.value = "2048";
1058
+ await el.saveMaxTokens();
1059
+ expect(mockSetMaxTokens).toHaveBeenCalledWith(
1060
+ orch,
1061
+ expect.anything(),
1062
+ 2048,
1063
+ );
1064
+
1065
+ // 6. saveSubagentWorkspaceMode
1066
+ const wsModeSelect = el.shadowRoot?.querySelector(
1067
+ '[data-setting="subagent-workspace-mode-select"]',
1068
+ ) as HTMLSelectElement | null;
1069
+ if (wsModeSelect) wsModeSelect.value = "isolated";
1070
+ await el.saveSubagentWorkspaceMode();
1071
+ expect(setConfig).toHaveBeenCalledWith(
1072
+ expect.anything(),
1073
+ "subagent_workspace_mode",
1074
+ "isolated",
1075
+ );
1076
+
1077
+ // 7. saveApiKey
1078
+ const keyInput = el.shadowRoot?.querySelector(
1079
+ '[data-setting="api-key-input"]',
1080
+ ) as HTMLInputElement | null;
1081
+ const providerSelect = el.shadowRoot?.querySelector(
1082
+ '[data-setting="provider-select"]',
1083
+ ) as HTMLSelectElement | null;
1084
+ if (keyInput) keyInput.value = "sk-test-key-123";
1085
+ if (providerSelect) providerSelect.value = "provider-a";
1086
+ await el.saveApiKey();
1087
+ expect(orch.setApiKey).toHaveBeenCalledWith(
1088
+ expect.anything(),
1089
+ "sk-test-key-123",
1090
+ );
1091
+
1092
+ // 8. saveBuiltinAiSettings
1093
+ await el.saveBuiltinAiSettings();
1094
+ expect(setConfig).toHaveBeenCalled();
1095
+
1096
+ // 9. saveTransformersJsSettings
1097
+ await el.saveTransformersJsSettings();
1098
+ expect(setConfig).toHaveBeenCalled();
1099
+
1100
+ // 10. showLlamafileHelpDialog
1101
+ el.requestAppDialog = jest.fn<any>().mockResolvedValue(undefined);
1102
+ await el.showLlamafileHelpDialog("connection refused");
1103
+ expect(el.requestAppDialog).toHaveBeenCalled();
1104
+
1105
+ // 11. saveMeshLlmSettings
1106
+ const hostInput = el.shadowRoot?.querySelector(
1107
+ '[data-setting="mesh-llm-host-input"]',
1108
+ ) as HTMLInputElement | null;
1109
+ if (hostInput) hostInput.value = "http://localhost:11434";
1110
+ await el.saveMeshLlmSettings();
1111
+
1112
+ // 12. updateModelSelector dynamic models
1113
+ (global.fetch as any) = jest.fn<any>().mockResolvedValue({
1114
+ ok: true,
1115
+ status: 200,
1116
+ json: async () => ({
1117
+ models: [{ id: "dynamic-model-1", name: "Dynamic Model 1" }],
1118
+ }),
1119
+ });
1120
+ orch.getProvider.mockReturnValue("bedrock_proxy");
1121
+ el.updateModelSelector();
1122
+
1123
+ document.body.removeChild(el);
1124
+ });
822
1125
  });
@@ -318,16 +318,16 @@ export class ShadowClawLlm extends ShadowClawElement {
318
318
  }
319
319
 
320
320
  const modeInput = root.querySelector(
321
- '[data-setting="llamafile-mode"]',
321
+ '[data-setting="llamafile-mode"], [data-setting="llamafile-mode-select"]',
322
322
  ) as HTMLSelectElement | null;
323
323
  const hostInput = root.querySelector(
324
- '[data-setting="llamafile-host"]',
324
+ '[data-setting="llamafile-host"], [data-setting="llamafile-host-input"]',
325
325
  ) as HTMLInputElement | null;
326
326
  const portInput = root.querySelector(
327
- '[data-setting="llamafile-port"]',
327
+ '[data-setting="llamafile-port"], [data-setting="llamafile-port-input"]',
328
328
  ) as HTMLInputElement | null;
329
329
  const offlineInput = root.querySelector(
330
- '[data-setting="llamafile-offline"]',
330
+ '[data-setting="llamafile-offline"], [data-setting="llamafile-offline-toggle"]',
331
331
  ) as HTMLInputElement | null;
332
332
 
333
333
  if (modeInput) {
@@ -1694,16 +1694,16 @@ export class ShadowClawLlm extends ShadowClawElement {
1694
1694
  }
1695
1695
 
1696
1696
  const modeInput = root.querySelector(
1697
- '[data-setting="llamafile-mode"]',
1697
+ '[data-setting="llamafile-mode"], [data-setting="llamafile-mode-select"]',
1698
1698
  ) as HTMLSelectElement | null;
1699
1699
  const hostInput = root.querySelector(
1700
- '[data-setting="llamafile-host"]',
1700
+ '[data-setting="llamafile-host"], [data-setting="llamafile-host-input"]',
1701
1701
  ) as HTMLInputElement | null;
1702
1702
  const portInput = root.querySelector(
1703
- '[data-setting="llamafile-port"]',
1703
+ '[data-setting="llamafile-port"], [data-setting="llamafile-port-input"]',
1704
1704
  ) as HTMLInputElement | null;
1705
1705
  const offlineInput = root.querySelector(
1706
- '[data-setting="llamafile-offline"]',
1706
+ '[data-setting="llamafile-offline"], [data-setting="llamafile-offline-toggle"]',
1707
1707
  ) as HTMLInputElement | null;
1708
1708
 
1709
1709
  if (!modeInput || !hostInput || !portInput || !offlineInput) {
@@ -291,4 +291,35 @@ describe("shadow-claw-mcp-remote", () => {
291
291
 
292
292
  document.body.removeChild(el);
293
293
  });
294
+
295
+ it("handles deleteConnection, reconnectOAuth, and testConnection", async () => {
296
+ mockDeleteConnection.mockResolvedValue(true);
297
+ mockReconnectMcpOAuth.mockResolvedValue({ success: true });
298
+ mockTestConnection.mockResolvedValue({
299
+ success: true,
300
+ toolCount: 2,
301
+ toolNames: ["search", "fetch"],
302
+ steps: [{ step: "Ping", status: "ok" }],
303
+ });
304
+
305
+ const el = new ShadowClawMcpRemote() as any;
306
+ document.body.appendChild(el);
307
+ await el.connectedCallback();
308
+ el.db = {} as any;
309
+ el.connections = [{ id: "conn-1", label: "My Connection" }];
310
+
311
+ // 1. Delete connection
312
+ await el.deleteConnection("conn-1");
313
+ expect(mockDeleteConnection).toHaveBeenCalledWith(el.db, "conn-1");
314
+
315
+ // 2. Reconnect OAuth
316
+ await el.reconnectOAuth("conn-1");
317
+ expect(mockReconnectMcpOAuth).toHaveBeenCalledWith(el.db, "conn-1");
318
+
319
+ // 3. Test connection
320
+ await el.testConnection("conn-1");
321
+ expect(mockTestConnection).toHaveBeenCalledWith(el.db, "conn-1");
322
+
323
+ document.body.removeChild(el);
324
+ });
294
325
  });
@@ -11,6 +11,9 @@ jest.unstable_mockModule(
11
11
  getPushUrl: jest
12
12
  .fn<any>()
13
13
  .mockImplementation((path: string) => Promise.resolve(path)),
14
+ getPushFetchOptions: jest
15
+ .fn<any>()
16
+ .mockImplementation((_url: string, opts?: any) => opts || {}),
14
17
  }),
15
18
  );
16
19
 
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  getCurrentSubscription,
3
+ getPushFetchOptions,
3
4
  getPushUrl,
4
5
  subscribeToPush,
5
6
  unsubscribeFromPush,
@@ -186,9 +187,12 @@ export class ShadowClawNotifications extends ShadowClawElement {
186
187
 
187
188
  try {
188
189
  const url = await getPushUrl(`/push/subscription/${this._selectedId}`);
189
- const res = await fetch(url, {
190
- method: "DELETE",
191
- });
190
+ const res = await fetch(
191
+ url,
192
+ getPushFetchOptions(url, {
193
+ method: "DELETE",
194
+ }),
195
+ );
192
196
 
193
197
  if (res.ok) {
194
198
  showSuccess("Subscription deleted.");
@@ -237,11 +241,14 @@ export class ShadowClawNotifications extends ShadowClawElement {
237
241
 
238
242
  try {
239
243
  const url = await getPushUrl("/push/send");
240
- const res = await fetch(url, {
241
- method: "POST",
242
- headers: { "Content-Type": "application/json" },
243
- body: JSON.stringify({ endpoint: sub.endpoint, payload }),
244
- });
244
+ const res = await fetch(
245
+ url,
246
+ getPushFetchOptions(url, {
247
+ method: "POST",
248
+ headers: { "Content-Type": "application/json" },
249
+ body: JSON.stringify({ endpoint: sub.endpoint, payload }),
250
+ }),
251
+ );
245
252
 
246
253
  if (res.ok) {
247
254
  showSuccess("Notification sent!");
@@ -303,10 +310,37 @@ export class ShadowClawNotifications extends ShadowClawElement {
303
310
  }
304
311
 
305
312
  try {
313
+ const isStaticHost =
314
+ typeof location !== "undefined" &&
315
+ (location.protocol === "file:" ||
316
+ location.hostname.endsWith(".github.io") ||
317
+ location.hostname.endsWith(".pages.dev"));
318
+
319
+ const db = await getDb();
320
+ const proxyUrl =
321
+ (await getConfig(db, CONFIG_KEYS.PUSH_PROXY_URL)) ||
322
+ (typeof localStorage !== "undefined"
323
+ ? localStorage.getItem(CONFIG_KEYS.PUSH_PROXY_URL)
324
+ : null);
325
+
326
+ if (isStaticHost && !proxyUrl) {
327
+ this._subscriptions = [];
328
+ this._backendAvailable = false;
329
+ this._selectedId = null;
330
+ this.renderSubscriptionList();
331
+ this.updateActionButtons();
332
+ return;
333
+ }
334
+
306
335
  const url = await getPushUrl("/push/subscriptions");
307
- const res = await fetch(url);
336
+ const res = await fetch(url, getPushFetchOptions(url));
308
337
 
309
338
  if (!res.ok) {
339
+ this._subscriptions = [];
340
+ this._backendAvailable = false;
341
+ this._selectedId = null;
342
+ this.renderSubscriptionList();
343
+ this.updateActionButtons();
310
344
  return;
311
345
  }
312
346