shadow-claw 1.40.0 → 1.41.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 (373) hide show
  1. package/.well-known/mcp/server-card.json +2 -2
  2. package/.well-known/mcp/servers.json +1 -1
  3. package/.well-known/mcp.json +2 -2
  4. package/README.md +1 -0
  5. package/dist/cli/build/build.js +1 -1
  6. package/dist/cli/{chunk-Bfz2NYgD.js → chunk-0IeC97mU.js} +7 -7
  7. package/dist/cli/chunk-3oLxlxPn.js +1 -0
  8. package/dist/cli/{chunk-BXg4-9Rq2.js → chunk-8CrZyP1b2.js} +1 -1
  9. package/dist/cli/{chunk-BP5Hg6CM.js → chunk-B--ueArB.js} +1 -1
  10. package/dist/cli/chunk-BIh7rXlJ2.js +1 -0
  11. package/dist/cli/{chunk-Drr0VSGA2.js → chunk-BKnTOLLS2.js} +1 -1
  12. package/dist/cli/{chunk-C5PuRV08.js → chunk-BLMC1Bxv.js} +1 -1
  13. package/dist/cli/{chunk-B2tNSlFy2.js → chunk-BOigzz2U2.js} +1 -1
  14. package/dist/cli/{chunk-DOf3DZdR.js → chunk-BOzn335y.js} +1 -1
  15. package/dist/cli/chunk-BjOeVLb42.js +1 -0
  16. package/dist/cli/{chunk-DqTx0rRp2.js → chunk-Bjqh5_SU2.js} +1 -1
  17. package/dist/cli/chunk-BqWBd_jF2.js +1 -0
  18. package/dist/cli/{chunk-Bc9nRNyN.js → chunk-BvVUnHTq.js} +12 -9
  19. package/dist/cli/{chunk-DIbSoQmd2.js → chunk-C6FFByBU2.js} +2 -2
  20. package/dist/cli/{chunk-Drq18VZe.js → chunk-C9-aL3rF.js} +1 -1
  21. package/dist/cli/{chunk-BUhqHeEA2.js → chunk-C9czddEh2.js} +2 -2
  22. package/dist/cli/chunk-CCx8WOwz2.js +1 -0
  23. package/dist/cli/{chunk-BkMSjVJb.js → chunk-CK6ejMig.js} +1 -1
  24. package/dist/cli/chunk-CUUERioR2.js +1 -0
  25. package/dist/cli/chunk-CXAobnJd.js +1 -0
  26. package/dist/cli/chunk-CY1X0pOF2.js +1 -0
  27. package/dist/cli/chunk-CZvvgLVv.js +1 -0
  28. package/dist/cli/chunk-CbcwbfJB.js +1 -0
  29. package/dist/cli/{chunk-quilB3TV2.js → chunk-Ci9OJGcd2.js} +1 -1
  30. package/dist/cli/chunk-Cj9wl0qx.js +1 -0
  31. package/dist/cli/{chunk-3gRhOmGo2.js → chunk-CjJsVrKI2.js} +1 -1
  32. package/dist/cli/{chunk-CmEfzm-_.js → chunk-ClPZ4Rea.js} +1 -1
  33. package/dist/cli/{chunk-WMPAfIvV2.js → chunk-DHJCGRuG2.js} +1 -1
  34. package/dist/cli/{chunk-C7Y2xIk4.js → chunk-DKVkYTV9.js} +1 -1
  35. package/dist/cli/chunk-DR2XtWRo.js +1 -0
  36. package/dist/cli/{chunk-DID5VvOG.js → chunk-DTES-dE6.js} +3 -3
  37. package/dist/cli/{chunk-BiVJVh_S.js → chunk-DUlIqkaX.js} +7 -7
  38. package/dist/cli/{chunk-CkpueGaZ2.js → chunk-DjWle8FF2.js} +1 -1
  39. package/dist/cli/{chunk-CSvHmNOk.js → chunk-DmRXVLfy.js} +1 -1
  40. package/dist/cli/{chunk-BT1xXZn72.js → chunk-HlSiTC-u2.js} +1 -1
  41. package/dist/cli/{chunk-da0bsiOn.js → chunk-P4JSeFBI.js} +1 -1
  42. package/dist/cli/chunk-UcNbHaRQ.js +1 -0
  43. package/dist/cli/{chunk-BXFHFbEd.js → chunk-UweLmfQS.js} +1 -1
  44. package/dist/cli/chunk-bQQkVCw-.js +94 -0
  45. package/dist/cli/{chunk-CGiXWHTi2.js → chunk-gwpxl-0W2.js} +1 -1
  46. package/dist/cli/{chunk-NH8koEis2.js → chunk-nmDhxXH_2.js} +1 -1
  47. package/dist/cli/chunk-p0n_c-I2.js +1 -0
  48. package/dist/cli/{chunk-D7xP72La.js → chunk-q7r4FosZ.js} +1 -1
  49. package/dist/cli/cli.js +3 -3
  50. package/dist/cli/prerender/dsd-shell/prerender-dsd-shell.js +1 -1
  51. package/dist/electron/main.cjs +1 -1
  52. package/dist/headless-agent.js +1 -1
  53. package/dist/lib/{app-routes-BAEMLu9g.js → app-routes-CKkiX12g.js} +7 -2
  54. package/dist/lib/app-routes-CKkiX12g.js.map +1 -0
  55. package/dist/lib/components/index.js +26 -26
  56. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  57. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  58. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  59. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  60. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  61. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  62. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  63. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  64. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  65. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  66. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  67. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  68. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  69. package/dist/lib/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.d.ts +10 -0
  70. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  71. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  72. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  73. package/dist/lib/components/shadow-claw-channels.js +1 -1
  74. package/dist/lib/components/shadow-claw-chat.js +1 -1
  75. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  76. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  77. package/dist/lib/components/shadow-claw-file-viewer/shadow-claw-file-viewer.d.ts +1 -1
  78. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  79. package/dist/lib/components/shadow-claw-files.js +1 -1
  80. package/dist/lib/components/shadow-claw-git.js +1 -1
  81. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  82. package/dist/lib/components/shadow-claw-llm.js +1 -1
  83. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  84. package/dist/lib/components/shadow-claw-networking.js +1 -1
  85. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  86. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  87. package/dist/lib/components/shadow-claw-pages.js +1 -1
  88. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  89. package/dist/lib/components/shadow-claw-settings.js +1 -1
  90. package/dist/lib/components/shadow-claw-storage.js +1 -1
  91. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  92. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  93. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  94. package/dist/lib/components/shadow-claw-tools.js +1 -1
  95. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  96. package/dist/lib/components/shadow-claw.js +1 -1
  97. package/dist/lib/{config-CPHDmyTx.js → config-DZkF9LiO.js} +2 -2
  98. package/dist/lib/{config-CPHDmyTx.js.map → config-DZkF9LiO.js.map} +1 -1
  99. package/dist/lib/{configurePeerJs-BsJoycQd.js → configurePeerJs-B3IE9BY2.js} +3 -3
  100. package/dist/lib/{configurePeerJs-BsJoycQd.js.map → configurePeerJs-B3IE9BY2.js.map} +1 -1
  101. package/dist/lib/{connections-BdIru7bF.js → connections-CalJGWvZ.js} +2 -2
  102. package/dist/lib/{connections-BdIru7bF.js.map → connections-CalJGWvZ.js.map} +1 -1
  103. package/dist/lib/core/orchestrator/orchestrator-state.d.ts +2 -1
  104. package/dist/lib/core/orchestrator/orchestrator.d.ts +2 -1
  105. package/dist/lib/core/orchestrator/utils/operations/task.d.ts +1 -0
  106. package/dist/lib/{downloadGroupFile-Dn0KsMlV.js → downloadGroupFile-DDE1fIhJ.js} +2 -2
  107. package/dist/lib/{downloadGroupFile-Dn0KsMlV.js.map → downloadGroupFile-DDE1fIhJ.js.map} +1 -1
  108. package/dist/lib/{file-viewer-BYrXXu-X.js → file-viewer-Bll2EoGc.js} +5 -5
  109. package/dist/lib/{file-viewer-BYrXXu-X.js.map → file-viewer-Bll2EoGc.js.map} +1 -1
  110. package/dist/lib/{getGroupDir-DF-RZO8X.js → getGroupDir-MHv7Lvmp.js} +4 -4
  111. package/dist/lib/{getGroupDir-DF-RZO8X.js.map → getGroupDir-MHv7Lvmp.js.map} +1 -1
  112. package/dist/lib/{git-s_0cgKm1.js → git-Bv_M3dgw.js} +3 -3
  113. package/dist/lib/{git-s_0cgKm1.js.map → git-Bv_M3dgw.js.map} +1 -1
  114. package/dist/lib/{iframe-sanitizer-DFdda1jJ.js → iframe-sanitizer-C7ZGDjEI.js} +2 -2
  115. package/dist/lib/{iframe-sanitizer-DFdda1jJ.js.map → iframe-sanitizer-C7ZGDjEI.js.map} +1 -1
  116. package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js → iframe-storage-proxy-CR8ZYHPd.js} +2 -2
  117. package/dist/lib/{iframe-storage-proxy-qrhZmQyO.js.map → iframe-storage-proxy-CR8ZYHPd.js.map} +1 -1
  118. package/dist/lib/index.js +27 -27
  119. package/dist/lib/{initChatSplitResize-DuNlwCuI.js → initChatSplitResize-DiKTI-wk.js} +2 -2
  120. package/dist/lib/{initChatSplitResize-DuNlwCuI.js.map → initChatSplitResize-DiKTI-wk.js.map} +1 -1
  121. package/dist/lib/{initControlPlane-D4UZ4EhV.js → initControlPlane-CzjtQ4aM.js} +15 -15
  122. package/dist/lib/{initControlPlane-D4UZ4EhV.js.map → initControlPlane-CzjtQ4aM.js.map} +1 -1
  123. package/dist/lib/{markdown-CRRwbN9h.js → markdown-k8wA9LKy.js} +4 -4
  124. package/dist/lib/{markdown-CRRwbN9h.js.map → markdown-k8wA9LKy.js.map} +1 -1
  125. package/dist/lib/{mcp-reconnect-COdZ-cea.js → mcp-reconnect-CGIdfZnS.js} +2 -2
  126. package/dist/lib/{mcp-reconnect-COdZ-cea.js.map → mcp-reconnect-CGIdfZnS.js.map} +1 -1
  127. package/dist/lib/{memoryStorage-BsMe2FOQ.js → memoryStorage-DL-Bo3dh.js} +2 -2
  128. package/dist/lib/{memoryStorage-BsMe2FOQ.js.map → memoryStorage-DL-Bo3dh.js.map} +1 -1
  129. package/dist/lib/{orchestrator-D9f5fDCb.js → orchestrator-DKHCoikY.js} +11 -11
  130. package/dist/lib/{orchestrator-D9f5fDCb.js.map → orchestrator-DKHCoikY.js.map} +1 -1
  131. package/dist/lib/{orchestrator-Bq9ft3uG.js → orchestrator-Dn1ZOz9B.js} +20 -24
  132. package/dist/lib/orchestrator-Dn1ZOz9B.js.map +1 -0
  133. package/dist/lib/{peerjs-6u8KtenE.js → peerjs-BK41iBvP.js} +7 -7
  134. package/dist/lib/{peerjs-6u8KtenE.js.map → peerjs-BK41iBvP.js.map} +1 -1
  135. package/dist/lib/{push-client-DCnB2-OU.js → push-client-DKgVFD2Z.js} +3 -3
  136. package/dist/lib/{push-client-DCnB2-OU.js.map → push-client-DKgVFD2Z.js.map} +1 -1
  137. package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js → readGroupFileBytes-CgU_UfGv.js} +2 -2
  138. package/dist/lib/{readGroupFileBytes-nKNJ6sbl.js.map → readGroupFileBytes-CgU_UfGv.js.map} +1 -1
  139. package/dist/lib/{shadow-claw-MumteAJy.js → shadow-claw-CtCj5Rn_.js} +121 -16
  140. package/dist/lib/shadow-claw-CtCj5Rn_.js.map +1 -0
  141. package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js → shadow-claw-a2ui-CvK5w940.js} +2 -2
  142. package/dist/lib/{shadow-claw-a2ui-Dipe8nY2.js.map → shadow-claw-a2ui-CvK5w940.js.map} +1 -1
  143. package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js → shadow-claw-accounts-Br3XdHro.js} +3 -3
  144. package/dist/lib/{shadow-claw-accounts-BgCkz3Zv.js.map → shadow-claw-accounts-Br3XdHro.js.map} +1 -1
  145. package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js → shadow-claw-channel-config-Da2dN19M.js} +3 -3
  146. package/dist/lib/{shadow-claw-channel-config-CIm_Bxd2.js.map → shadow-claw-channel-config-Da2dN19M.js.map} +1 -1
  147. package/dist/lib/{shadow-claw-channels-CaceK52s.js → shadow-claw-channels-BofYHgz8.js} +3 -3
  148. package/dist/lib/{shadow-claw-channels-CaceK52s.js.map → shadow-claw-channels-BofYHgz8.js.map} +1 -1
  149. package/dist/lib/{shadow-claw-chat-BD18-mAR.js → shadow-claw-chat-Dw32DPV2.js} +12 -12
  150. package/dist/lib/{shadow-claw-chat-BD18-mAR.js.map → shadow-claw-chat-Dw32DPV2.js.map} +1 -1
  151. package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js → shadow-claw-control-plane-CwFotFDh.js} +7 -7
  152. package/dist/lib/{shadow-claw-control-plane-U6LsfRPm.js.map → shadow-claw-control-plane-CwFotFDh.js.map} +1 -1
  153. package/dist/lib/{shadow-claw-conversations-CDsza_oB.js → shadow-claw-conversations-0UXge23k.js} +5 -5
  154. package/dist/lib/{shadow-claw-conversations-CDsza_oB.js.map → shadow-claw-conversations-0UXge23k.js.map} +1 -1
  155. package/dist/lib/{shadow-claw-file-viewer-CMOnFmcP.js → shadow-claw-file-viewer-CAsn9NxH.js} +30 -30
  156. package/dist/lib/{shadow-claw-file-viewer-CMOnFmcP.js.map → shadow-claw-file-viewer-CAsn9NxH.js.map} +1 -1
  157. package/dist/lib/{shadow-claw-files-BI0P3m8a.js → shadow-claw-files-xGGhsHNH.js} +9 -9
  158. package/dist/lib/{shadow-claw-files-BI0P3m8a.js.map → shadow-claw-files-xGGhsHNH.js.map} +1 -1
  159. package/dist/lib/{shadow-claw-git-xRApLEMN.js → shadow-claw-git-B606hcMU.js} +6 -6
  160. package/dist/lib/{shadow-claw-git-xRApLEMN.js.map → shadow-claw-git-B606hcMU.js.map} +1 -1
  161. package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js → shadow-claw-integrations-Bwq5cba1.js} +4 -4
  162. package/dist/lib/{shadow-claw-integrations-B2e1Nfg1.js.map → shadow-claw-integrations-Bwq5cba1.js.map} +1 -1
  163. package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js → shadow-claw-llm-BgpcMBIy.js} +6 -6
  164. package/dist/lib/{shadow-claw-llm-Bc2mWuGb.js.map → shadow-claw-llm-BgpcMBIy.js.map} +1 -1
  165. package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js → shadow-claw-mcp-remote-DvQCA-Bk.js} +4 -4
  166. package/dist/lib/{shadow-claw-mcp-remote-D_Bh3aeC.js.map → shadow-claw-mcp-remote-DvQCA-Bk.js.map} +1 -1
  167. package/dist/lib/{shadow-claw-networking-F5W3n5ci.js → shadow-claw-networking-Cxvf9c7E.js} +2 -2
  168. package/dist/lib/{shadow-claw-networking-F5W3n5ci.js.map → shadow-claw-networking-Cxvf9c7E.js.map} +1 -1
  169. package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js → shadow-claw-notifications-CGVcFYBy.js} +3 -3
  170. package/dist/lib/{shadow-claw-notifications-lvMt9WJ1.js.map → shadow-claw-notifications-CGVcFYBy.js.map} +1 -1
  171. package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js → shadow-claw-page-header-DAYz3I_o.js} +2 -2
  172. package/dist/lib/{shadow-claw-page-header-6gq6EMqX.js.map → shadow-claw-page-header-DAYz3I_o.js.map} +1 -1
  173. package/dist/lib/{shadow-claw-pages-Bo8_z2S-.js → shadow-claw-pages-BLeEZb9k.js} +24 -12
  174. package/dist/lib/shadow-claw-pages-BLeEZb9k.js.map +1 -0
  175. package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js → shadow-claw-peerjs-BcFnbK74.js} +3 -3
  176. package/dist/lib/{shadow-claw-peerjs-DRTLCVDQ.js.map → shadow-claw-peerjs-BcFnbK74.js.map} +1 -1
  177. package/dist/lib/{shadow-claw-settings-D00B4p4L.js → shadow-claw-settings-B7o0Z4hu.js} +9 -9
  178. package/dist/lib/{shadow-claw-settings-D00B4p4L.js.map → shadow-claw-settings-B7o0Z4hu.js.map} +1 -1
  179. package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js → shadow-claw-storage-afMVqB7E.js} +5 -5
  180. package/dist/lib/{shadow-claw-storage-DgMe7-Zo.js.map → shadow-claw-storage-afMVqB7E.js.map} +1 -1
  181. package/dist/lib/{shadow-claw-task-server-D103YOuI.js → shadow-claw-task-server-B9-MQjl6.js} +2 -2
  182. package/dist/lib/{shadow-claw-task-server-D103YOuI.js.map → shadow-claw-task-server-B9-MQjl6.js.map} +1 -1
  183. package/dist/lib/{shadow-claw-tasks-6OhALkq1.js → shadow-claw-tasks-Dc4BC-t-.js} +8 -8
  184. package/dist/lib/{shadow-claw-tasks-6OhALkq1.js.map → shadow-claw-tasks-Dc4BC-t-.js.map} +1 -1
  185. package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js → shadow-claw-terminal-BINcTdsk.js} +2 -2
  186. package/dist/lib/{shadow-claw-terminal-BGtxA1qD.js.map → shadow-claw-terminal-BINcTdsk.js.map} +1 -1
  187. package/dist/lib/{shadow-claw-tools-OeIwAnnd.js → shadow-claw-tools-BjFgXNLR.js} +7 -7
  188. package/dist/lib/{shadow-claw-tools-OeIwAnnd.js.map → shadow-claw-tools-BjFgXNLR.js.map} +1 -1
  189. package/dist/lib/{shadow-claw-webvm-gVFZ197u.js → shadow-claw-webvm-Xe3ByMDw.js} +3 -3
  190. package/dist/lib/{shadow-claw-webvm-gVFZ197u.js.map → shadow-claw-webvm-Xe3ByMDw.js.map} +1 -1
  191. package/dist/lib/subsystems/mcp/webmcp.d.ts +64 -0
  192. package/dist/lib/{syncWebMcpRegistration-Bbph0XZl.js → syncWebMcpRegistration-D7alChz0.js} +21 -6
  193. package/dist/lib/syncWebMcpRegistration-D7alChz0.js.map +1 -0
  194. package/dist/lib/{tools-D0OlSzMD.js → tools-Cmhpk3X8.js} +4 -4
  195. package/dist/lib/{tools-D0OlSzMD.js.map → tools-Cmhpk3X8.js.map} +1 -1
  196. package/dist/lib/utils/index.js +1 -1
  197. package/dist/lib/utils/namespacedStorage.js +1 -1
  198. package/dist/lib/utils/utils.js +1 -1
  199. package/dist/lib/{utils-DYdrO2KI.js → utils-CzgKe_XZ.js} +4 -4
  200. package/dist/lib/{utils-DYdrO2KI.js.map → utils-CzgKe_XZ.js.map} +1 -1
  201. package/dist/lib/{webmcp-D2bocsgh.js → webmcp-oEP9YpR2.js} +119 -3
  202. package/dist/lib/{webmcp-D2bocsgh.js.map → webmcp-oEP9YpR2.js.map} +1 -1
  203. package/dist/public/.well-known/mcp/server-card.json +2 -2
  204. package/dist/public/.well-known/mcp/servers.json +1 -1
  205. package/dist/public/.well-known/mcp.json +2 -2
  206. package/dist/public/AGENTS.md +2 -1
  207. package/dist/public/README.md +1 -0
  208. package/dist/public/agent.worker.js +1 -1
  209. package/dist/public/app-routes-C6-H2zPH.js +1 -0
  210. package/dist/public/components/shadow-claw/shadow-claw.css +1 -1
  211. package/dist/public/components/shadow-claw/shadow-claw.html +1 -1
  212. package/dist/public/components/shadow-claw-pages/shadow-claw-pages.css +1 -1
  213. package/dist/public/{config-D4lVqMs8.js → config-Dewnqegf.js} +1 -1
  214. package/dist/public/{configurePeerJs-DDOAVSaH.js → configurePeerJs-CaPU2sN5.js} +1 -1
  215. package/dist/public/{connections-CiukjGiH.js → connections-CwE7jE9l.js} +1 -1
  216. package/dist/public/{custom-element-security-piU2U-Ec.js → custom-element-security-DLgHMf5R.js} +1 -1
  217. package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
  218. package/dist/public/docs/example/article/index.html +5 -5
  219. package/dist/public/docs/guides/publishing-to-github-pages.md +49 -6
  220. package/dist/public/docs/publishing/index.html +5 -5
  221. package/dist/public/docs/skill-creator/index.html +5 -5
  222. package/dist/public/docs/subsystems/pages.md +10 -3
  223. package/dist/public/docs/subsystems/webmcp.md +89 -1
  224. package/dist/public/{downloadGroupFile-BiZQYY-O.js → downloadGroupFile-ChWHuap_.js} +1 -1
  225. package/dist/public/e2e/README.md +2 -2
  226. package/dist/public/{e2e-bridge-CybB810Q.js → e2e-bridge-BkaldjV2.js} +1 -1
  227. package/dist/public/{file-viewer-DLUBvOVD.js → file-viewer-Df4qUJCn.js} +1 -1
  228. package/dist/public/{getGroupDir-CLz5c8cg.js → getGroupDir-CeSr4fzS.js} +1 -1
  229. package/dist/public/{git-Ba8AeviI.js → git-Bscc1tK-.js} +2 -2
  230. package/dist/public/{iframe-sanitizer-CKOO7cxp.js → iframe-sanitizer-BVdkQsor.js} +1 -1
  231. package/dist/public/{iframe-storage-proxy-BvRiccH0.js → iframe-storage-proxy-Cg8aFw7o.js} +1 -1
  232. package/dist/public/index.css +1 -1
  233. package/dist/public/index.html +5 -5
  234. package/dist/public/index.js +1 -1
  235. package/dist/public/{initChatSplitResize-osJXM2gu.js → initChatSplitResize-C3Rp1LeZ.js} +1 -1
  236. package/dist/public/{initControlPlane-CNggzsRw.js → initControlPlane-C1pnzG16.js} +1 -1
  237. package/dist/public/llms.txt +1 -1
  238. package/dist/public/main/index.html +5 -5
  239. package/dist/public/main/memory/index.html +5 -5
  240. package/dist/public/{markdown-CZA-Ye2Q.js → markdown-D3A902nT.js} +1 -1
  241. package/dist/public/{mcp-reconnect-BK1bYBdC.js → mcp-reconnect-BJWxQBm_.js} +1 -1
  242. package/dist/public/{memoryStorage-Bew1X8Uy.js → memoryStorage-ykv-Ri-y.js} +1 -1
  243. package/dist/public/{orchestrator-D6tel2Zp.js → orchestrator-BVnC4Vtt.js} +2 -2
  244. package/dist/public/{orchestrator-BLkS_jpm.js → orchestrator-CQcc7OFA.js} +2 -2
  245. package/dist/public/{peerjs-CYs1F7sq.js → peerjs-CYC2cI1Y.js} +1 -1
  246. package/dist/public/{push-client-_HUyJlFJ.js → push-client-BJrnOULM.js} +1 -1
  247. package/dist/public/service-worker/fetch-proxy.js +1 -1
  248. package/dist/public/service-worker.js +1 -1
  249. package/dist/public/{shadow-claw-DmrxnX3Q.js → shadow-claw-WwfOu-tu.js} +33 -3
  250. package/dist/public/{shadow-claw-accounts-BqGrm47Z.js → shadow-claw-accounts-BFZxrPsH.js} +1 -1
  251. package/dist/public/{shadow-claw-channels-Ci2UPaaA.js → shadow-claw-channels-C0w0c1EF.js} +1 -1
  252. package/dist/public/{shadow-claw-chat-BxR6cBYQ.js → shadow-claw-chat-DIJ0A-QN.js} +1 -1
  253. package/dist/public/{shadow-claw-control-plane-C1A-CZ9H.js → shadow-claw-control-plane-DZoxUd-1.js} +2 -2
  254. package/dist/public/{shadow-claw-conversations-DcwG05-H.js → shadow-claw-conversations-DMyG_5zj.js} +1 -1
  255. package/dist/public/{shadow-claw-file-viewer-a_uRA30F.js → shadow-claw-file-viewer-DYgZjZb9.js} +2 -2
  256. package/dist/public/{shadow-claw-files-B774Xz1d.js → shadow-claw-files-DIOUdKJr.js} +1 -1
  257. package/dist/public/{shadow-claw-git-CTIPP0vs.js → shadow-claw-git-if5S0QJr.js} +2 -2
  258. package/dist/public/{shadow-claw-integrations-CjtkA6l0.js → shadow-claw-integrations-BQt4hcWe.js} +1 -1
  259. package/dist/public/{shadow-claw-llm-DfmITHde.js → shadow-claw-llm-9uf1KFA0.js} +2 -2
  260. package/dist/public/{shadow-claw-mcp-remote-BPuV22So.js → shadow-claw-mcp-remote-COpNIzJd.js} +1 -1
  261. package/dist/public/{shadow-claw-networking-DEMMygy0.js → shadow-claw-networking-AOVRUn6B.js} +1 -1
  262. package/dist/public/{shadow-claw-notifications-B9Xhcj1P.js → shadow-claw-notifications-CKB-W1TA.js} +1 -1
  263. package/dist/public/{shadow-claw-page-header-CAXokjQy.js → shadow-claw-page-header-D4LmUigA.js} +1 -1
  264. package/dist/public/{shadow-claw-pages-BRQw8bTm.js → shadow-claw-pages-BpHRY4vH.js} +12 -3
  265. package/dist/public/{shadow-claw-settings-BGrDNyqh.js → shadow-claw-settings-CE-aUo_J.js} +3 -3
  266. package/dist/public/{shadow-claw-storage-DK3pf11w.js → shadow-claw-storage-CFKC-Y3V.js} +1 -1
  267. package/dist/public/{shadow-claw-task-server-CMp5Vjza.js → shadow-claw-task-server-B_ZjqK4B.js} +1 -1
  268. package/dist/public/{shadow-claw-tasks-BO8aXM2B.js → shadow-claw-tasks-CGsEv48K.js} +1 -1
  269. package/dist/public/{shadow-claw-terminal-BSeTuyDa.js → shadow-claw-terminal-DcaS7cPH.js} +1 -1
  270. package/dist/public/{shadow-claw-tools-Bj6DLZ-2.js → shadow-claw-tools-Cnu9hKy0.js} +1 -1
  271. package/dist/public/{shadow-claw-webvm-M7d5RH8H.js → shadow-claw-webvm-BB8SFvJ8.js} +1 -1
  272. package/dist/public/syncWebMcpRegistration-D2Zx4oA1.js +1 -0
  273. package/dist/public/theme-init.js +1 -1
  274. package/dist/public/{tools-nysPKnGT.js → tools-sOLCHTj8.js} +1 -1
  275. package/dist/public/{webmcp-Bo3KTgRb.js → webmcp-YFt6vM1_.js} +1 -1
  276. package/dist/server.js +189 -189
  277. package/package.json +1 -1
  278. package/src/cli/cli.ts +2 -2
  279. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +4 -1
  280. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +7 -1
  281. package/src/cli/prerender/utils/insert-before-closing-head.test.ts +12 -0
  282. package/src/cli/prerender/utils/insert-before-closing-head.ts +4 -6
  283. package/src/cli/site-config/apply.test.ts +90 -0
  284. package/src/cli/site-config/apply.ts +181 -13
  285. package/src/components/shadow-claw/shadow-claw.css +26 -0
  286. package/src/components/shadow-claw/shadow-claw.html +4 -0
  287. package/src/components/shadow-claw/utils/applyRoute.ts +3 -0
  288. package/src/components/shadow-claw/utils/bindEventListeners.test.ts +6 -0
  289. package/src/components/shadow-claw/utils/bindEventListeners.ts +30 -1
  290. package/src/components/shadow-claw/utils/getRoute.test.ts +52 -0
  291. package/src/components/shadow-claw/utils/getRoute.ts +18 -0
  292. package/src/components/shadow-claw/utils/navigateToRoute.test.ts +21 -0
  293. package/src/components/shadow-claw/utils/navigateToRoute.ts +10 -0
  294. package/src/components/shadow-claw/utils/sidebarVisibility.ts +12 -4
  295. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.test.ts +93 -0
  296. package/src/components/shadow-claw/utils/syncSlottedSidebarActiveLinks.ts +100 -0
  297. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +31 -31
  298. package/src/components/shadow-claw-pages/shadow-claw-pages.css +10 -1
  299. package/src/components/shadow-claw-pages/utils/removePreviewIframe.ts +3 -0
  300. package/src/core/effect.test.ts +19 -0
  301. package/src/core/orchestrator/orchestrator-state.ts +2 -1
  302. package/src/core/orchestrator/orchestrator.test.ts +686 -5
  303. package/src/core/orchestrator/orchestrator.ts +3 -10
  304. package/src/core/orchestrator/utils/compactContext.test.ts +423 -121
  305. package/src/core/orchestrator/utils/configurePeerJs.test.ts +27 -0
  306. package/src/core/orchestrator/utils/deliverResponse.test.ts +127 -0
  307. package/src/core/orchestrator/utils/dispatchSubagentInvoke.test.ts +23 -0
  308. package/src/core/orchestrator/utils/enqueue.test.ts +520 -86
  309. package/src/core/orchestrator/utils/handleWorkerMessage.test.ts +664 -144
  310. package/src/core/orchestrator/utils/initTasks.node.test.ts +147 -0
  311. package/src/core/orchestrator/utils/initTasks.test.ts +926 -191
  312. package/src/core/orchestrator/utils/initTasks.ts +5 -4
  313. package/src/core/orchestrator/utils/invokeAgent.test.ts +563 -110
  314. package/src/core/orchestrator/utils/invokeAgent.ts +8 -10
  315. package/src/core/orchestrator/utils/loadChannelConfigurations.test.ts +50 -0
  316. package/src/core/orchestrator/utils/operations/channel.test.ts +377 -170
  317. package/src/core/orchestrator/utils/operations/provider.test.ts +663 -85
  318. package/src/core/orchestrator/utils/operations/room.test.ts +74 -11
  319. package/src/core/orchestrator/utils/operations/task.test.ts +452 -56
  320. package/src/core/orchestrator/utils/operations/task.ts +1 -1
  321. package/src/core/orchestrator/utils/operations/vm.test.ts +66 -7
  322. package/src/core/orchestrator/utils/parseDirectToolCommand.test.ts +6 -0
  323. package/src/core/orchestrator/utils/settings.test.ts +248 -81
  324. package/src/core/orchestrator/utils/setupPushTaskListener.test.ts +58 -0
  325. package/src/core/orchestrator/utils/syncWebMcpRegistration.test.ts +392 -64
  326. package/src/core/orchestrator/utils/syncWebMcpRegistration.ts +32 -3
  327. package/src/server/middleware/static-files.test.ts +36 -0
  328. package/src/server/middleware/static-files.ts +14 -6
  329. package/src/server/routes/mcp.ts +2 -2
  330. package/src/storage/staticRouting.test.ts +36 -0
  331. package/src/storage/staticRouting.ts +31 -3
  332. package/src/stores/file-viewer.test.ts +11 -0
  333. package/src/stores/theme.test.ts +41 -2
  334. package/src/stores/toast.test.ts +25 -0
  335. package/src/subsystems/mcp/webmcp.test.ts +255 -0
  336. package/src/subsystems/mcp/webmcp.ts +253 -0
  337. package/src/worker/tools/bash/native-bash-executor.test.ts +31 -1
  338. package/src/worker/tools/git/utils/extractConflictPaths.test.ts +24 -0
  339. package/src/worker/tools/git/utils/parseConflictRegions.test.ts +46 -0
  340. package/src/worker/tools/git/utils/resolveCorsProxy.test.ts +26 -5
  341. package/src/worker/tools/git/utils/truncateSnippet.test.ts +16 -0
  342. package/src/worker/tools/node-transformers-executor.test.ts +105 -6
  343. package/src/worker/tools/remote-mcp/remote-mcp.test.ts +172 -47
  344. package/src/worker/tools/remote-mcp/utils/formatListToolsOutput.test.ts +19 -0
  345. package/src/worker/tools/remote-mcp/utils/isMcpReauthError.test.ts +45 -0
  346. package/src/worker/tools/remote-mcp/utils/resolveMcpReauth.test.ts +48 -0
  347. package/src/worker/tools/rooms/list-room-members.test.ts +24 -10
  348. package/src/worker/tools/tasks/list-tasks.test.ts +39 -8
  349. package/src/worker/tools/tasks/update-task.test.ts +78 -14
  350. package/src/worker/tools/ui/javascript.test.ts +70 -17
  351. package/src/worker/tools/ui/list-tool-profiles.test.ts +31 -9
  352. package/dist/cli/chunk-00GAzVPl2.js +0 -1
  353. package/dist/cli/chunk-2hnFoNNC.js +0 -1
  354. package/dist/cli/chunk-BKQL4nzF2.js +0 -1
  355. package/dist/cli/chunk-BQ4K3fqj2.js +0 -1
  356. package/dist/cli/chunk-BVCGo6uf2.js +0 -1
  357. package/dist/cli/chunk-BcTH_-FL.js +0 -1
  358. package/dist/cli/chunk-BmD_sPRt.js +0 -1
  359. package/dist/cli/chunk-BsxNqqer.js +0 -1
  360. package/dist/cli/chunk-CCcF5T-c.js +0 -1
  361. package/dist/cli/chunk-CFhvwUeL.js +0 -91
  362. package/dist/cli/chunk-CwWtvULn.js +0 -1
  363. package/dist/cli/chunk-Drf-hXIR2.js +0 -1
  364. package/dist/cli/chunk-Dt5HNYVi.js +0 -1
  365. package/dist/cli/chunk-DwZQi9Ax.js +0 -1
  366. package/dist/cli/chunk-m9ZX7TyF2.js +0 -1
  367. package/dist/lib/app-routes-BAEMLu9g.js.map +0 -1
  368. package/dist/lib/orchestrator-Bq9ft3uG.js.map +0 -1
  369. package/dist/lib/shadow-claw-MumteAJy.js.map +0 -1
  370. package/dist/lib/shadow-claw-pages-Bo8_z2S-.js.map +0 -1
  371. package/dist/lib/syncWebMcpRegistration-Bbph0XZl.js.map +0 -1
  372. package/dist/public/app-routes-BMQ4HFaM.js +0 -1
  373. package/dist/public/syncWebMcpRegistration-4bT0YUyZ.js +0 -1
@@ -61,6 +61,18 @@ describe("setupPushTaskListener", () => {
61
61
  {} as ShadowClawDatabase,
62
62
  ),
63
63
  ).not.toThrow();
64
+
65
+ Object.defineProperty(global, "navigator", {
66
+ value: {},
67
+ configurable: true,
68
+ });
69
+
70
+ expect(() =>
71
+ setupPushTaskListener(
72
+ mockOrchestrator as Orchestrator,
73
+ {} as ShadowClawDatabase,
74
+ ),
75
+ ).not.toThrow();
64
76
  });
65
77
 
66
78
  it("should register a message listener if serviceWorker exists", () => {
@@ -203,6 +215,52 @@ describe("setupPushTaskListener", () => {
203
215
  );
204
216
  });
205
217
 
218
+ it("should ignore events without data or without groupId", () => {
219
+ listener({ data: null });
220
+ listener({ data: { type: "scheduled-task-trigger", taskId: "t1" } });
221
+ expect(mockOrchestrator.schedulerTriggeredGroups?.size).toBe(0);
222
+ });
223
+
224
+ it("should create default taskId and prompt for tool task when omitted", async () => {
225
+ mockOrchestratorStore.tasks = [];
226
+
227
+ listener({
228
+ data: {
229
+ type: "scheduled-task-trigger",
230
+ groupId: "g2",
231
+ taskType: "tools",
232
+ tools: ["t1"],
233
+ },
234
+ });
235
+
236
+ await new Promise(process.nextTick);
237
+
238
+ expect(mockOrchestratorStore.runTask).toHaveBeenCalledWith(
239
+ expect.objectContaining({
240
+ groupId: "g2",
241
+ type: "tools",
242
+ tools: ["t1"],
243
+ prompt: "",
244
+ }),
245
+ );
246
+ });
247
+
248
+ it("should do nothing if neither task, tools, nor prompt are present", async () => {
249
+ mockOrchestratorStore.tasks = [];
250
+
251
+ listener({
252
+ data: {
253
+ type: "scheduled-task-trigger",
254
+ groupId: "g-empty",
255
+ },
256
+ });
257
+
258
+ await new Promise(process.nextTick);
259
+
260
+ expect(mockOrchestratorStore.runTask).not.toHaveBeenCalled();
261
+ expect(mockSubmitMessage).not.toHaveBeenCalled();
262
+ });
263
+
206
264
  it("should handle error in runTaskHandler", async () => {
207
265
  mockOrchestratorStore.tasks = [];
208
266
  mockSubmitMessage.mockImplementation(() => {
@@ -1,34 +1,87 @@
1
1
  import { beforeEach, describe, expect, it, jest } from "@jest/globals";
2
2
 
3
- const mockRegisterWebMcpTools = jest.fn() as any;
4
- const mockUnregisterWebMcpTools = jest.fn() as any;
3
+ import { CONFIG_KEYS } from "../../../config/config.js";
4
+ import type { ShadowClawDatabase } from "../../../db/db.js";
5
+ import type { DeclarativeToolDefinition } from "../../../subsystems/tools/declarative.js";
6
+ import type { SkillRecord } from "../../../subsystems/skills/types.js";
7
+ import type { ToolDefinition } from "../../../subsystems/tools/tools.js";
8
+ import type { WebMcpMode } from "../../../subsystems/mcp/webmcp.js";
9
+ import { activate_skill } from "../../../subsystems/skills/tool.js";
10
+ import type { Orchestrator } from "../orchestrator.js";
11
+
12
+ const mockRegisterWebMcpTools = jest
13
+ .fn<
14
+ (
15
+ worker: Worker | null,
16
+ onMessage: (msg: unknown) => Promise<void>,
17
+ activeGroupId: string,
18
+ tools: ToolDefinition[],
19
+ ) => Promise<void>
20
+ >()
21
+ .mockResolvedValue(undefined);
22
+
23
+ const mockUnregisterWebMcpTools = jest.fn<() => void>();
24
+ const mockApplyWebMcpMode = jest.fn<(mode: WebMcpMode) => void>();
25
+
26
+ let lastActivatedListener: ((event: any) => void) | null = null;
27
+ let lastCancelListener: ((event: any) => void) | null = null;
28
+ const mockUnsubscribeActivated = jest.fn<() => void>();
29
+ const mockUnsubscribeCancel = jest.fn<() => void>();
30
+ const mockOnWebMcpToolActivated = jest.fn((cb: any) => {
31
+ lastActivatedListener = cb;
32
+ return mockUnsubscribeActivated;
33
+ });
34
+ const mockOnWebMcpToolCancel = jest.fn((cb: any) => {
35
+ lastCancelListener = cb;
36
+ return mockUnsubscribeCancel;
37
+ });
5
38
 
6
39
  jest.unstable_mockModule("../../../subsystems/mcp/webmcp.js", () => ({
7
40
  registerWebMcpTools: mockRegisterWebMcpTools,
8
41
  unregisterWebMcpTools: mockUnregisterWebMcpTools,
9
- setWebMcpMode: jest.fn(),
42
+ setWebMcpMode: mockApplyWebMcpMode,
43
+ onWebMcpToolActivated: mockOnWebMcpToolActivated,
44
+ onWebMcpToolCancel: mockOnWebMcpToolCancel,
10
45
  }));
11
46
 
12
- const mockHandleWorkerMessage = jest.fn() as any;
47
+ const mockHandleWorkerMessage = jest
48
+ .fn<
49
+ (
50
+ orchestrator: Orchestrator,
51
+ db: ShadowClawDatabase,
52
+ msg: unknown,
53
+ ) => Promise<void>
54
+ >()
55
+ .mockResolvedValue(undefined);
56
+
13
57
  jest.unstable_mockModule("./handleWorkerMessage.js", () => ({
14
58
  handleWorkerMessage: mockHandleWorkerMessage,
15
59
  }));
16
60
 
17
61
  jest.unstable_mockModule("../../effect.js", () => ({
18
- effect: (cb: any) => {
62
+ effect: (cb: () => void) => {
19
63
  cb();
20
64
  return jest.fn();
21
65
  },
22
66
  }));
23
67
 
24
- jest.unstable_mockModule("../../../stores/orchestrator.js", () => ({
25
- orchestratorStore: {
26
- activeGroupId: "group1",
27
- groups: [{ groupId: "group1", toolTags: ["tool1"] }, { groupId: "group2" }],
28
- },
68
+ const mockSetConfig = jest
69
+ .fn<(db: ShadowClawDatabase, key: string, value: string) => Promise<void>>()
70
+ .mockResolvedValue(undefined);
71
+
72
+ jest.unstable_mockModule("../../../db/setConfig.js", () => ({
73
+ setConfig: mockSetConfig,
29
74
  }));
30
75
 
31
- const mockDiscoverSkills = jest.fn() as any;
76
+ const mockDiscoverSkills = jest
77
+ .fn<
78
+ (
79
+ db: ShadowClawDatabase,
80
+ groupId: string,
81
+ ) => Promise<{ skills: SkillRecord[] }>
82
+ >()
83
+ .mockResolvedValue({ skills: [] });
84
+
32
85
  jest.unstable_mockModule(
33
86
  "../../../subsystems/skills/discoverSkills.js",
34
87
  () => ({
@@ -36,79 +89,184 @@ jest.unstable_mockModule(
36
89
  }),
37
90
  );
38
91
 
39
- const mockLoadDeclarativeTools = jest.fn() as any;
92
+ const mockLoadDeclarativeTools = jest
93
+ .fn<
94
+ (
95
+ db: ShadowClawDatabase,
96
+ groupId: string,
97
+ ) => Promise<{ tools: DeclarativeToolDefinition[] }>
98
+ >()
99
+ .mockResolvedValue({ tools: [] });
100
+
40
101
  jest.unstable_mockModule("../../../subsystems/tools/declarative.js", () => ({
41
102
  loadDeclarativeTools: mockLoadDeclarativeTools,
42
103
  }));
43
104
 
105
+ let activeGroupId = "group1";
106
+ const mockGroups: { groupId: string; toolTags?: string[] }[] = [
107
+ { groupId: "group1", toolTags: ["tool1", "enabled_decl_tool"] },
108
+ { groupId: "group2" },
109
+ ];
110
+ const mockAllTools: ToolDefinition[] = [
111
+ {
112
+ name: "tool1",
113
+ description: "Tool 1",
114
+ input_schema: { type: "object", properties: {} },
115
+ },
116
+ {
117
+ name: "tool2",
118
+ description: "Tool 2",
119
+ input_schema: { type: "object", properties: {} },
120
+ },
121
+ ];
122
+ let mockGlobalTools: ToolDefinition[] = [
123
+ {
124
+ name: "tool2",
125
+ description: "Tool 2",
126
+ input_schema: { type: "object", properties: {} },
127
+ },
128
+ ];
129
+ let declarativeToolsFail = false;
130
+
131
+ jest.unstable_mockModule("../../../stores/orchestrator.js", () => ({
132
+ orchestratorStore: {
133
+ get activeGroupId() {
134
+ return activeGroupId;
135
+ },
136
+ get groups() {
137
+ return mockGroups;
138
+ },
139
+ },
140
+ }));
141
+
44
142
  jest.unstable_mockModule("../../../stores/tools.js", () => ({
45
143
  toolsStore: {
46
- allTools: [{ name: "tool1" }, { name: "tool2" }],
47
- enabledTools: [{ name: "tool2" }],
144
+ get allTools() {
145
+ return mockAllTools;
146
+ },
147
+ get enabledTools() {
148
+ return mockGlobalTools;
149
+ },
48
150
  declarativeTools: [],
49
151
  declarativeToolNamesEnabled: null,
50
- refreshDeclarativeTools: jest.fn(async (db: any, groupId: any) => {
51
- const res = await mockLoadDeclarativeTools(db, groupId);
52
- return res?.tools || [];
53
- }),
54
- isDeclarativeToolEnabled: jest.fn(() => true),
152
+ refreshDeclarativeTools: jest.fn(
153
+ async (db: ShadowClawDatabase, gId: string) => {
154
+ if (declarativeToolsFail) {
155
+ throw new Error("OPFS declarative tools load failed");
156
+ }
157
+ const res = await mockLoadDeclarativeTools(db, gId);
158
+ return res?.tools || [];
159
+ },
160
+ ),
161
+ isDeclarativeToolEnabled: jest.fn(
162
+ (name: string) => name === "enabled_decl_tool",
163
+ ),
55
164
  },
56
165
  }));
57
166
 
58
- const { syncWebMcpRegistration } = await import("./syncWebMcpRegistration.js");
167
+ const { syncWebMcpRegistration, setWebMcpMode, setWebMcpToolsEnabled } =
168
+ await import("./syncWebMcpRegistration.js");
169
+
170
+ interface MockOrchestrator {
171
+ webMcpEffectCleanup: (() => void) | null;
172
+ webMcpToolsEnabled: boolean;
173
+ webMcpRegistrationLock: Promise<void>;
174
+ agentWorker: Worker | null;
175
+ events?: { emit: jest.Mock };
176
+ }
59
177
 
60
178
  describe("syncWebMcpRegistration", () => {
61
- let mockOrchestrator: any;
62
- let mockDb: any;
179
+ let mockOrchestrator: MockOrchestrator;
180
+ let mockDb: ShadowClawDatabase;
63
181
 
64
182
  beforeEach(() => {
65
183
  jest.clearAllMocks();
66
- mockDb = {};
184
+ activeGroupId = "group1";
185
+ declarativeToolsFail = false;
186
+ mockGlobalTools = [
187
+ {
188
+ name: "tool2",
189
+ description: "Tool 2",
190
+ input_schema: { type: "object", properties: {} },
191
+ },
192
+ ];
193
+ mockDb = {} as unknown as ShadowClawDatabase;
67
194
  mockOrchestrator = {
68
195
  webMcpEffectCleanup: jest.fn(),
69
196
  webMcpToolsEnabled: true,
70
197
  webMcpRegistrationLock: Promise.resolve(),
71
- agentWorker: {},
72
- handleWorkerMessage: jest.fn(),
198
+ agentWorker: {} as unknown as Worker,
199
+ events: { emit: jest.fn() },
73
200
  };
74
201
  mockDiscoverSkills.mockResolvedValue({ skills: [] });
75
202
  mockLoadDeclarativeTools.mockResolvedValue({
76
203
  tools: [
77
204
  {
78
- name: "generate_random_number",
79
- description: "Generate random number",
205
+ name: "enabled_decl_tool",
206
+ description: "Enabled Declarative Tool",
207
+ input_schema: { type: "object", properties: {} },
208
+ execution: { type: "bash", command: "echo test" },
209
+ },
210
+ {
211
+ name: "disabled_decl_tool",
212
+ description: "Disabled Declarative Tool",
213
+ input_schema: { type: "object", properties: {} },
214
+ execution: { type: "bash", command: "echo disabled" },
80
215
  },
81
- { name: "unpinned_decl_tool", description: "Unpinned tool" },
82
216
  ],
83
217
  });
84
218
  });
85
219
 
86
220
  it("should clean up existing effect and unregister if not enabled", () => {
87
221
  mockOrchestrator.webMcpToolsEnabled = false;
222
+ const cleanupMock = mockOrchestrator.webMcpEffectCleanup!;
88
223
 
89
- const cleanupMock = mockOrchestrator.webMcpEffectCleanup;
90
-
91
- syncWebMcpRegistration(mockOrchestrator, mockDb);
224
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
92
225
 
93
226
  expect(cleanupMock).toHaveBeenCalled();
94
227
  expect(mockOrchestrator.webMcpEffectCleanup).toBeNull();
95
228
  expect(mockUnregisterWebMcpTools).toHaveBeenCalled();
96
229
  });
97
230
 
98
- it("should setup effect and register tools (including declarative tools filtered by toolTags) if enabled", async () => {
99
- // Add generate_random_number to group1's toolTags
100
- const orchestratorStoreModule =
101
- await import("../../../stores/orchestrator.js");
102
- orchestratorStoreModule.orchestratorStore.groups[0].toolTags = [
103
- "tool1",
104
- "generate_random_number",
105
- ];
231
+ it("should subscribe to toolactivated and toolcancel events and emit on orchestrator.events", async () => {
232
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
233
+ await mockOrchestrator.webMcpRegistrationLock;
106
234
 
107
- syncWebMcpRegistration(mockOrchestrator, mockDb);
235
+ expect(mockOnWebMcpToolActivated).toHaveBeenCalled();
236
+ expect(mockOnWebMcpToolCancel).toHaveBeenCalled();
108
237
 
109
- expect(mockOrchestrator.webMcpEffectCleanup).toBeDefined();
238
+ // Trigger activated listener
239
+ lastActivatedListener?.({ toolName: "read_file" });
240
+ expect(mockOrchestrator.events?.emit).toHaveBeenCalledWith(
241
+ "webmcp-tool-activated",
242
+ { toolName: "read_file" },
243
+ );
110
244
 
111
- // The effect should run and chain onto webMcpRegistrationLock
245
+ // Trigger cancel listener
246
+ lastCancelListener?.({ toolName: "read_file" });
247
+ expect(mockOrchestrator.events?.emit).toHaveBeenCalledWith(
248
+ "webmcp-tool-cancel",
249
+ { toolName: "read_file" },
250
+ );
251
+ });
252
+
253
+ it("should unsubscribe from toolactivated and toolcancel when webMcpEffectCleanup is executed", async () => {
254
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
255
+ await mockOrchestrator.webMcpRegistrationLock;
256
+
257
+ const cleanup = mockOrchestrator.webMcpEffectCleanup;
258
+ expect(cleanup).toBeDefined();
259
+
260
+ cleanup!();
261
+
262
+ expect(mockUnsubscribeActivated).toHaveBeenCalled();
263
+ expect(mockUnsubscribeCancel).toHaveBeenCalled();
264
+ });
265
+
266
+ it("should setup effect and register tools filtered by group toolTags", async () => {
267
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
268
+
269
+ expect(mockOrchestrator.webMcpEffectCleanup).toBeDefined();
112
270
  await mockOrchestrator.webMcpRegistrationLock;
113
271
 
114
272
  expect(mockUnregisterWebMcpTools).toHaveBeenCalled();
@@ -117,47 +275,100 @@ describe("syncWebMcpRegistration", () => {
117
275
  expect.any(Function),
118
276
  "group1",
119
277
  [
120
- { name: "tool1" },
121
278
  {
122
- name: "generate_random_number",
123
- description: "Generate random number",
279
+ name: "tool1",
280
+ description: "Tool 1",
281
+ input_schema: { type: "object", properties: {} },
124
282
  },
283
+ expect.objectContaining({
284
+ name: "enabled_decl_tool",
285
+ description: "Enabled Declarative Tool",
286
+ }),
125
287
  ],
126
288
  );
127
289
 
128
- // Call the callback to test coverage of handleWorkerMessage
129
- const cb = mockRegisterWebMcpTools.mock.calls[0][1];
130
- await cb("test message");
290
+ const onMessageCb = mockRegisterWebMcpTools.mock.calls[0][1];
291
+ await onMessageCb({ type: "worker-message" });
131
292
  expect(mockHandleWorkerMessage).toHaveBeenCalledWith(
132
- mockOrchestrator,
293
+ mockOrchestrator as unknown as Orchestrator,
133
294
  mockDb,
134
- "test message",
295
+ { type: "worker-message" },
135
296
  );
136
297
  });
137
298
 
138
- it("should handle error in registration lock promise", async () => {
139
- mockRegisterWebMcpTools.mockRejectedValueOnce(new Error("Test error"));
140
- const consoleError = jest
141
- .spyOn(console, "error")
142
- .mockImplementation(() => {});
299
+ it("should register globalTools and enabledDeclarativeTools when group has no toolTags", async () => {
300
+ activeGroupId = "group2";
143
301
 
144
- syncWebMcpRegistration(mockOrchestrator, mockDb);
302
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
303
+ await mockOrchestrator.webMcpRegistrationLock;
145
304
 
305
+ expect(mockRegisterWebMcpTools).toHaveBeenCalledWith(
306
+ mockOrchestrator.agentWorker,
307
+ expect.any(Function),
308
+ "group2",
309
+ [
310
+ {
311
+ name: "tool2",
312
+ description: "Tool 2",
313
+ input_schema: { type: "object", properties: {} },
314
+ },
315
+ expect.objectContaining({
316
+ name: "enabled_decl_tool",
317
+ description: "Enabled Declarative Tool",
318
+ }),
319
+ ],
320
+ );
321
+ });
322
+
323
+ it("should register globalTools and enabledDeclarativeTools when activeGroupId is not found in groups", async () => {
324
+ activeGroupId = "unknown-group";
325
+
326
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
146
327
  await mockOrchestrator.webMcpRegistrationLock;
147
- expect(consoleError).toHaveBeenCalledWith(
148
- "WebMCP registration failed:",
149
- expect.any(Error),
328
+
329
+ expect(mockRegisterWebMcpTools).toHaveBeenCalledWith(
330
+ mockOrchestrator.agentWorker,
331
+ expect.any(Function),
332
+ "unknown-group",
333
+ [
334
+ {
335
+ name: "tool2",
336
+ description: "Tool 2",
337
+ input_schema: { type: "object", properties: {} },
338
+ },
339
+ expect.objectContaining({
340
+ name: "enabled_decl_tool",
341
+ description: "Enabled Declarative Tool",
342
+ }),
343
+ ],
150
344
  );
345
+ });
151
346
 
152
- consoleError.mockRestore();
347
+ it("should ignore OPFS load errors in refreshDeclarativeTools and discoverSkills", async () => {
348
+ declarativeToolsFail = true;
349
+ mockDiscoverSkills.mockRejectedValueOnce(new Error("OPFS skills failure"));
350
+
351
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
352
+ await mockOrchestrator.webMcpRegistrationLock;
353
+
354
+ expect(mockRegisterWebMcpTools).toHaveBeenCalled();
153
355
  });
154
356
 
155
- it("should append activate_skill tool to WebMCP tools when skills are discovered", async () => {
156
- mockDiscoverSkills.mockResolvedValue({
157
- skills: [{ name: "my-skill", description: "My custom skill" }],
357
+ it("should append activate_skill when skills are discovered and tool is not present", async () => {
358
+ mockDiscoverSkills.mockResolvedValueOnce({
359
+ skills: [
360
+ {
361
+ name: "test-skill",
362
+ description: "Test skill description",
363
+ path: "skills/test/SKILL.md",
364
+ basePath: "skills/test",
365
+ userInvocable: true,
366
+ disableModelInvocation: false,
367
+ },
368
+ ],
158
369
  });
159
370
 
160
- syncWebMcpRegistration(mockOrchestrator, mockDb);
371
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
161
372
  await mockOrchestrator.webMcpRegistrationLock;
162
373
 
163
374
  expect(mockRegisterWebMcpTools).toHaveBeenCalledWith(
@@ -169,4 +380,121 @@ describe("syncWebMcpRegistration", () => {
169
380
  ]),
170
381
  );
171
382
  });
383
+
384
+ it("should not duplicate activate_skill if already present in tool list", async () => {
385
+ mockDiscoverSkills.mockResolvedValueOnce({
386
+ skills: [
387
+ {
388
+ name: "test-skill",
389
+ description: "Test skill description",
390
+ path: "skills/test/SKILL.md",
391
+ basePath: "skills/test",
392
+ userInvocable: true,
393
+ disableModelInvocation: false,
394
+ },
395
+ ],
396
+ });
397
+ activeGroupId = "group2";
398
+ mockGlobalTools = [
399
+ {
400
+ name: "tool2",
401
+ description: "Tool 2",
402
+ input_schema: { type: "object", properties: {} },
403
+ },
404
+ activate_skill,
405
+ ];
406
+
407
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
408
+ await mockOrchestrator.webMcpRegistrationLock;
409
+
410
+ const registeredTools = mockRegisterWebMcpTools.mock.calls[0][3];
411
+ const activateSkillCount = registeredTools.filter(
412
+ (t) => t.name === "activate_skill",
413
+ ).length;
414
+ expect(activateSkillCount).toBe(1);
415
+ });
416
+
417
+ it("should catch and log errors in registration lock promise", async () => {
418
+ mockRegisterWebMcpTools.mockRejectedValueOnce(
419
+ new Error("Registration error"),
420
+ );
421
+ const consoleError = jest
422
+ .spyOn(console, "error")
423
+ .mockImplementation(() => {});
424
+
425
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
426
+ await mockOrchestrator.webMcpRegistrationLock;
427
+
428
+ expect(consoleError).toHaveBeenCalledWith(
429
+ "WebMCP registration failed:",
430
+ expect.any(Error),
431
+ );
432
+ consoleError.mockRestore();
433
+ });
434
+
435
+ it("should proceed without calling cleanup if webMcpEffectCleanup is null", async () => {
436
+ mockOrchestrator.webMcpEffectCleanup = null;
437
+
438
+ syncWebMcpRegistration(mockOrchestrator as unknown as Orchestrator, mockDb);
439
+ await mockOrchestrator.webMcpRegistrationLock;
440
+
441
+ expect(mockRegisterWebMcpTools).toHaveBeenCalled();
442
+ });
443
+
444
+ describe("setWebMcpMode", () => {
445
+ it("should unregister tools, apply mode, save config, and trigger sync", async () => {
446
+ const syncDeps = {
447
+ orchestrator: mockOrchestrator as unknown as Orchestrator,
448
+ };
449
+
450
+ await setWebMcpMode(mockOrchestrator, mockDb, "native", syncDeps);
451
+ await mockOrchestrator.webMcpRegistrationLock;
452
+
453
+ expect(mockUnregisterWebMcpTools).toHaveBeenCalled();
454
+ expect(mockApplyWebMcpMode).toHaveBeenCalledWith("native");
455
+ expect(mockSetConfig).toHaveBeenCalledWith(
456
+ mockDb,
457
+ CONFIG_KEYS.WEBMCP_MODE,
458
+ "native",
459
+ );
460
+ expect(mockRegisterWebMcpTools).toHaveBeenCalled();
461
+ });
462
+ });
463
+
464
+ describe("setWebMcpToolsEnabled", () => {
465
+ it("should update enabled state to true, persist config, and sync", async () => {
466
+ mockOrchestrator.webMcpToolsEnabled = false;
467
+ const syncDeps = {
468
+ orchestrator: mockOrchestrator as unknown as Orchestrator,
469
+ };
470
+
471
+ await setWebMcpToolsEnabled(mockOrchestrator, mockDb, true, syncDeps);
472
+ await mockOrchestrator.webMcpRegistrationLock;
473
+
474
+ expect(mockOrchestrator.webMcpToolsEnabled).toBe(true);
475
+ expect(mockSetConfig).toHaveBeenCalledWith(
476
+ mockDb,
477
+ CONFIG_KEYS.WEBMCP_TOOLS_ENABLED,
478
+ "true",
479
+ );
480
+ expect(mockRegisterWebMcpTools).toHaveBeenCalled();
481
+ });
482
+
483
+ it("should update enabled state to false, persist config, and sync", async () => {
484
+ mockOrchestrator.webMcpToolsEnabled = true;
485
+ const syncDeps = {
486
+ orchestrator: mockOrchestrator as unknown as Orchestrator,
487
+ };
488
+
489
+ await setWebMcpToolsEnabled(mockOrchestrator, mockDb, false, syncDeps);
490
+
491
+ expect(mockOrchestrator.webMcpToolsEnabled).toBe(false);
492
+ expect(mockSetConfig).toHaveBeenCalledWith(
493
+ mockDb,
494
+ CONFIG_KEYS.WEBMCP_TOOLS_ENABLED,
495
+ "false",
496
+ );
497
+ expect(mockUnregisterWebMcpTools).toHaveBeenCalled();
498
+ });
499
+ });
172
500
  });