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
@@ -78,6 +78,7 @@ import { ensureControlPlaneConnected } from "../utils/initControlPlane.js";
78
78
  import { applyAllChannelRunningStates } from "./utils/operations/channel.js";
79
79
 
80
80
  import type { ShadowClawDatabase } from "../../db/db.js";
81
+ import type { InboundMessage } from "../../subsystems/channels/types.js";
81
82
  import type { A2UIAction } from "../../ui/a2ui/types.js";
82
83
  import type { DirectToolCommandPolicy } from "./utils/types.js";
83
84
 
@@ -139,7 +140,7 @@ export class Orchestrator {
139
140
 
140
141
  meshLlmHost: string = "";
141
142
 
142
- messageQueue: any[] = [];
143
+ messageQueue: InboundMessage[] = [];
143
144
  model: string = getDefaultProvider().defaultModel;
144
145
 
145
146
  /** Peer groupIds where the A2A task has reached a terminal state */
@@ -503,15 +504,7 @@ export class Orchestrator {
503
504
  if (!storedKey) {
504
505
  this.#encryptedApiKey = "";
505
506
  } else {
506
- try {
507
- // We now store the encrypted key directly in the field.
508
- // decryptValue is only called on-demand.
509
- this.#encryptedApiKey = storedKey;
510
- } catch (e) {
511
- console.warn("[Orchestrator] Failed to load API key:", e);
512
-
513
- this.#encryptedApiKey = "";
514
- }
507
+ this.#encryptedApiKey = storedKey;
515
508
  }
516
509
 
517
510
  this.#apiKeyCache = null; // Invalidate cache
@@ -1,194 +1,496 @@
1
- import { jest } from "@jest/globals";
1
+ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
2
2
 
3
+ import { CONFIG_KEYS, DEFAULT_GROUP_ID } from "../../../config/config.js";
4
+ import type { ProviderConfig } from "../../../config/config.js";
5
+ import type { ConversationMessage } from "../../../content/types.js";
6
+ import type { ShadowClawDatabase } from "../../../db/db.js";
7
+ import type { GroupMeta } from "../../../db/types.js";
3
8
  import type { Orchestrator } from "../orchestrator.js";
4
9
 
10
+ const mockReadGroupFile =
11
+ jest.fn<
12
+ (
13
+ db: ShadowClawDatabase,
14
+ groupId: string,
15
+ filename: string,
16
+ ) => Promise<string>
17
+ >();
5
18
  jest.unstable_mockModule("../../../storage/readGroupFile.js", () => ({
6
- readGroupFile: jest.fn<any>().mockRejectedValue(new Error("no file")),
19
+ readGroupFile: mockReadGroupFile,
7
20
  }));
8
21
 
22
+ const mockBuildConversationMessages =
23
+ jest.fn<(groupId: string, limit: number) => Promise<ConversationMessage[]>>();
9
24
  jest.unstable_mockModule("../../../db/buildConversationMessages.js", () => ({
10
- buildConversationMessages: jest.fn<any>().mockResolvedValue([]),
25
+ buildConversationMessages: mockBuildConversationMessages,
11
26
  }));
12
27
 
28
+ const mockGetConfig =
29
+ jest.fn<(db: ShadowClawDatabase, key: string) => Promise<string | null>>();
13
30
  jest.unstable_mockModule("../../../db/getConfig.js", () => ({
14
- getConfig: jest.fn<any>().mockResolvedValue(null),
15
- }));
16
-
17
- jest.unstable_mockModule("../../../db/saveMessage.js", () => ({
18
- saveMessage: jest.fn<any>().mockResolvedValue(1),
31
+ getConfig: mockGetConfig,
19
32
  }));
20
33
 
34
+ let mockGroupsList: GroupMeta[] = [];
21
35
  jest.unstable_mockModule("../../../db/groups.js", () => ({
22
- getGroupMetadata: jest.fn<any>().mockResolvedValue([]),
23
- saveGroupMetadata: jest.fn<any>().mockResolvedValue(undefined),
24
- createGroup: jest.fn<any>().mockResolvedValue({} as any),
25
- renameGroup: jest.fn<any>().mockResolvedValue(undefined),
26
- updateGroupToolTags: jest.fn<any>().mockResolvedValue(undefined),
27
- deleteGroupMetadata: jest.fn<any>().mockResolvedValue(undefined),
28
- listGroups: jest.fn<any>().mockResolvedValue([
29
- {
30
- groupId: "group-pinned",
31
- pinnedProvider: "transformers_js_local",
32
- pinnedModel: "onnx-community/Llama-3.2-1B-Instruct",
33
- },
34
- ]),
35
- reorderGroups: jest.fn<any>().mockResolvedValue(undefined),
36
- cloneGroup: jest.fn<any>().mockResolvedValue({} as any),
37
- updateGroupPinnedProvider: jest.fn<any>().mockResolvedValue(undefined),
38
- updateGroupSubagentSettings: jest.fn<any>().mockResolvedValue(undefined),
36
+ getGroupMetadata: jest.fn<() => Promise<GroupMeta[]>>().mockResolvedValue([]),
37
+ saveGroupMetadata: jest
38
+ .fn<() => Promise<void>>()
39
+ .mockResolvedValue(undefined),
40
+ createGroup: jest
41
+ .fn<() => Promise<GroupMeta>>()
42
+ .mockResolvedValue({} as GroupMeta),
43
+ renameGroup: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
44
+ updateGroupToolTags: jest
45
+ .fn<() => Promise<void>>()
46
+ .mockResolvedValue(undefined),
47
+ deleteGroupMetadata: jest
48
+ .fn<() => Promise<void>>()
49
+ .mockResolvedValue(undefined),
50
+ listGroups: jest.fn<() => Promise<GroupMeta[]>>(async () => mockGroupsList),
51
+ reorderGroups: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
52
+ cloneGroup: jest
53
+ .fn<() => Promise<GroupMeta>>()
54
+ .mockResolvedValue({} as GroupMeta),
55
+ updateGroupPinnedProvider: jest
56
+ .fn<() => Promise<void>>()
57
+ .mockResolvedValue(undefined),
58
+ updateGroupSubagentSettings: jest
59
+ .fn<() => Promise<void>>()
60
+ .mockResolvedValue(undefined),
39
61
  updateGroupProviderRuntimeOverrides: jest
40
- .fn<any>()
62
+ .fn<() => Promise<void>>()
41
63
  .mockResolvedValue(undefined),
42
64
  }));
43
65
 
66
+ const mockEnsureBuiltinAiPolyfills = jest
67
+ .fn<() => Promise<void>>()
68
+ .mockResolvedValue(undefined);
69
+ const mockSummarizeText = jest
70
+ .fn<(text: string, options: unknown) => Promise<string>>()
71
+ .mockResolvedValue("Mock task summary");
44
72
  jest.unstable_mockModule(
45
73
  "../../../subsystems/providers/builtin-ai-tasks.js",
46
74
  () => ({
47
- ensureBuiltinAiPolyfills: jest.fn<any>().mockResolvedValue(undefined),
48
- summarizeText: jest
49
- .fn<any>()
50
- .mockResolvedValue("Mock summary from Task API"),
51
- writeText: jest.fn<any>().mockResolvedValue(""),
52
- rewriteText: jest.fn<any>().mockResolvedValue(""),
53
- detectLanguage: jest.fn<any>().mockResolvedValue([]),
54
- translateText: jest.fn<any>().mockResolvedValue(""),
55
- proofreadText: jest.fn<any>().mockResolvedValue(""),
56
- embedText: jest.fn<any>().mockResolvedValue([]),
57
- isBuiltinTaskSupported: jest.fn(() => true),
58
- getPromptApiFallbackModel: jest.fn<any>().mockResolvedValue(""),
59
- createTaskInstanceWithFallback: jest.fn<any>().mockResolvedValue({}),
60
- isWebGpuAdapterAvailable: jest.fn<any>().mockResolvedValue(false),
75
+ ensureBuiltinAiPolyfills: mockEnsureBuiltinAiPolyfills,
76
+ summarizeText: mockSummarizeText,
77
+ writeText: jest.fn<() => Promise<string>>().mockResolvedValue(""),
78
+ rewriteText: jest.fn<() => Promise<string>>().mockResolvedValue(""),
79
+ detectLanguage: jest.fn<() => Promise<unknown[]>>().mockResolvedValue([]),
80
+ translateText: jest.fn<() => Promise<string>>().mockResolvedValue(""),
81
+ proofreadText: jest.fn<() => Promise<string>>().mockResolvedValue(""),
82
+ embedText: jest.fn<() => Promise<unknown[]>>().mockResolvedValue([]),
83
+ isBuiltinTaskSupported: jest.fn<() => boolean>(() => true),
84
+ getPromptApiFallbackModel: jest
85
+ .fn<() => Promise<string>>()
86
+ .mockResolvedValue(""),
87
+ createTaskInstanceWithFallback: jest
88
+ .fn<() => Promise<unknown>>()
89
+ .mockResolvedValue({}),
90
+ isWebGpuAdapterAvailable: jest
91
+ .fn<() => Promise<boolean>>()
92
+ .mockResolvedValue(false),
61
93
  }),
62
94
  );
63
95
 
96
+ const mockIsPromptApiSupported = jest.fn<() => boolean>().mockReturnValue(true);
97
+ const mockCompactWithPromptApi = jest
98
+ .fn<
99
+ (
100
+ systemPrompt: string,
101
+ messages: unknown[],
102
+ signal: AbortSignal,
103
+ onMessage: (msg: unknown) => Promise<void>,
104
+ groupId: string,
105
+ ) => Promise<string>
106
+ >()
107
+ .mockResolvedValue("Mock prompt API summary");
108
+
64
109
  jest.unstable_mockModule(
65
110
  "../../../subsystems/providers/prompt-api-provider.js",
66
111
  () => ({
67
- isPromptApiSupported: jest.fn<any>().mockReturnValue(true),
68
- compactWithPromptApi: jest
69
- .fn<any>()
70
- .mockResolvedValue("Mock prompt API summary"),
71
- invokeWithPromptApi: jest.fn<any>().mockResolvedValue(undefined),
112
+ isPromptApiSupported: mockIsPromptApiSupported,
113
+ compactWithPromptApi: mockCompactWithPromptApi,
114
+ invokeWithPromptApi: jest
115
+ .fn<() => Promise<void>>()
116
+ .mockResolvedValue(undefined),
72
117
  }),
73
118
  );
74
119
 
120
+ const mockDeliverResponse = jest
121
+ .fn<
122
+ (
123
+ o: Orchestrator,
124
+ db: ShadowClawDatabase,
125
+ groupId: string,
126
+ text: string,
127
+ ) => Promise<void>
128
+ >()
129
+ .mockResolvedValue(undefined);
130
+
131
+ jest.unstable_mockModule("./deliverResponse.js", () => ({
132
+ deliverResponse: mockDeliverResponse,
133
+ }));
134
+
135
+ const mockHandleWorkerMessage = jest
136
+ .fn<
137
+ (o: Orchestrator, db: ShadowClawDatabase, msg: unknown) => Promise<void>
138
+ >()
139
+ .mockResolvedValue(undefined);
140
+
141
+ jest.unstable_mockModule("./handleWorkerMessage.js", () => ({
142
+ handleWorkerMessage: mockHandleWorkerMessage,
143
+ }));
144
+
145
+ let mockPeerState: unknown = null;
146
+ jest.unstable_mockModule("../../../stores/orchestrator.js", () => ({
147
+ orchestratorStore: {
148
+ getPeerState: jest.fn(() => mockPeerState),
149
+ },
150
+ }));
151
+
152
+ const { compactContext } = await import("./compactContext.js");
153
+ const { Orchestrator: Orch } = await import("../orchestrator.js");
154
+
75
155
  describe("compactContext", () => {
76
- let db: any;
156
+ let db: ShadowClawDatabase;
77
157
  let o: Orchestrator;
78
- let compactContext: any;
79
158
 
80
- beforeEach(async () => {
81
- db = {} as any;
82
- const { Orchestrator: Orch } = await import("../orchestrator.js");
159
+ beforeEach(() => {
160
+ jest.clearAllMocks();
161
+ db = {} as unknown as ShadowClawDatabase;
162
+ mockPeerState = null;
163
+ mockGroupsList = [
164
+ {
165
+ groupId: "group-pinned",
166
+ name: "Pinned Group",
167
+ createdAt: Date.now(),
168
+ pinnedProvider: "transformers_js_local",
169
+ pinnedModel: "onnx-community/Llama-3.2-1B-Instruct",
170
+ },
171
+ {
172
+ groupId: "group-pinned-no-model",
173
+ name: "Pinned Group No Model",
174
+ createdAt: Date.now(),
175
+ pinnedProvider: "openrouter",
176
+ },
177
+ {
178
+ groupId: "group-pinned-unknown-prov",
179
+ name: "Pinned Group Unknown Prov",
180
+ createdAt: Date.now(),
181
+ pinnedProvider: "unknown_prov_xyz",
182
+ },
183
+ ];
184
+
185
+ mockReadGroupFile.mockRejectedValue(new Error("File not found"));
186
+ mockBuildConversationMessages.mockResolvedValue([]);
187
+ mockGetConfig.mockResolvedValue(null);
188
+ mockIsPromptApiSupported.mockReturnValue(true);
189
+
83
190
  o = new Orch();
191
+ jest.spyOn(o, "getApiKey").mockResolvedValue("valid-api-key");
84
192
  o.provider = "openrouter";
85
- o.providerConfig = { requiresApiKey: false } as any;
86
-
87
- const mod = await import("./compactContext.js");
88
- compactContext = mod.compactContext;
193
+ o.model = "openai/gpt-4o";
194
+ o.providerConfig = {
195
+ id: "openrouter",
196
+ name: "OpenRouter",
197
+ defaultModel: "openai/gpt-4o",
198
+ requiresApiKey: false,
199
+ } as ProviderConfig;
200
+ o.agentWorker = { postMessage: jest.fn() } as unknown as Worker;
201
+ o.handleCompactDone = jest
202
+ .fn<() => Promise<void>>()
203
+ .mockResolvedValue(undefined);
89
204
  });
90
205
 
91
- it("emits error if requires API key and none is set", async () => {
206
+ it("should emit error and provider-help if provider requires API key and none is set", async () => {
92
207
  o.provider = "openrouter";
93
- o.providerConfig = { requiresApiKey: true } as any;
208
+ o.providerConfig = {
209
+ id: "openrouter",
210
+ name: "OpenRouter",
211
+ defaultModel: "openai/gpt-4o",
212
+ requiresApiKey: true,
213
+ } as ProviderConfig;
94
214
  jest.spyOn(o, "getApiKey").mockResolvedValue("");
95
215
 
96
- const events: any[] = [];
97
- o.events.on("error", (e: any) => events.push(e));
216
+ const helpEvents: unknown[] = [];
217
+ const errorEvents: unknown[] = [];
218
+ o.events.on("provider-help", (e) => helpEvents.push(e));
219
+ o.events.on("error", (e) => errorEvents.push(e));
98
220
 
99
221
  await compactContext(o, db, "group-1");
100
222
 
101
- expect(events).toHaveLength(1);
102
- expect(events[0].error).toMatch(/API key not configured/);
223
+ expect(helpEvents).toHaveLength(1);
224
+ expect(errorEvents).toHaveLength(1);
225
+ expect(errorEvents[0]).toEqual({
226
+ groupId: "group-1",
227
+ error: "API key not configured. Cannot compact context.",
228
+ });
103
229
  });
104
230
 
105
- it("emits error if state is not idle", async () => {
106
- jest.spyOn(o, "getApiKey").mockResolvedValue("key");
231
+ it("should fetch specific API key when effective provider differs from orchestrator provider", async () => {
232
+ const specificApiKeySpy = jest
233
+ .spyOn(o, "getApiKeyForSpecificProvider")
234
+ .mockResolvedValueOnce("specific-key");
235
+ mockGroupsList.push({
236
+ groupId: "group-different-prov",
237
+ name: "Diff Prov",
238
+ createdAt: Date.now(),
239
+ pinnedProvider: "anthropic",
240
+ pinnedModel: "claude-3-5-sonnet",
241
+ });
242
+
243
+ await compactContext(o, db, "group-different-prov");
244
+
245
+ expect(specificApiKeySpy).toHaveBeenCalledWith(db, "anthropic");
246
+ expect(o.agentWorker?.postMessage).toHaveBeenCalled();
247
+ });
248
+
249
+ it("should emit error and abort if orchestrator state is not idle", async () => {
107
250
  o.setState("thinking");
108
251
 
109
- const events: any[] = [];
110
- o.events.on("error", (e: any) => events.push(e));
252
+ const errorEvents: unknown[] = [];
253
+ o.events.on("error", (e) => errorEvents.push(e));
111
254
 
112
255
  await compactContext(o, db, "group-1");
113
256
 
114
- expect(events).toHaveLength(1);
115
- expect(events[0].error).toMatch(/Cannot compact while processing/);
257
+ expect(errorEvents).toHaveLength(1);
258
+ expect(errorEvents[0]).toEqual({
259
+ groupId: "group-1",
260
+ error:
261
+ "Cannot compact while processing. Wait for the current response to finish.",
262
+ });
116
263
  });
117
264
 
118
- it("sends message to agentWorker", async () => {
119
- jest.spyOn(o, "getApiKey").mockResolvedValue("key");
120
- const postMessage = jest.fn();
121
- o.agentWorker = { postMessage } as any;
265
+ it("should read memory from MEMORY.md if available", async () => {
266
+ mockReadGroupFile.mockResolvedValueOnce("# Context Memory");
122
267
 
123
268
  await compactContext(o, db, "group-1");
124
269
 
270
+ const postMessage = o.agentWorker?.postMessage as jest.Mock;
125
271
  expect(postMessage).toHaveBeenCalled();
126
- const payload: any = postMessage.mock.calls[0][0];
127
- expect(payload.type).toBe("compact");
128
- expect(payload.payload.groupId).toBe("group-1");
272
+ const payload = postMessage.mock.calls[0][0] as {
273
+ payload: { memory: string };
274
+ };
275
+ expect(payload.payload.memory).toBe("# Context Memory");
129
276
  });
130
277
 
131
- it("uses effective provider and model when group has pinned provider", async () => {
132
- jest.spyOn(o, "getApiKey").mockResolvedValue("key");
133
- const postMessage = jest.fn();
134
- o.agentWorker = { postMessage } as any;
135
-
136
- o.provider = "openrouter";
137
- o.model = "gpt-4o";
278
+ it("should use peerState from orchestratorStore when available", async () => {
279
+ mockPeerState = { peerId: "peer-abc", typing: true };
138
280
 
139
- await compactContext(o, db, "group-pinned");
281
+ await compactContext(o, db, "group-1");
140
282
 
141
- expect(postMessage).toHaveBeenCalled();
142
- const payload: any = postMessage.mock.calls[0][0];
143
- expect(payload.type).toBe("compact");
144
- expect(payload.payload.provider).toBe("transformers_js_local");
145
- expect(payload.payload.model).toBe("onnx-community/Llama-3.2-1B-Instruct");
283
+ expect(o.agentWorker?.postMessage).toHaveBeenCalled();
146
284
  });
147
285
 
148
- it("handles compactionPref === 'builtin_task_api'", async () => {
149
- jest.spyOn(o, "getApiKey").mockResolvedValue("key");
150
- const { getConfig } = await import("../../../db/getConfig.js");
151
- (getConfig as jest.Mock<any>).mockImplementation(
152
- (_db: any, key: string) => {
153
- if (key === "compaction_engine_preference") {
154
- return Promise.resolve("builtin_task_api");
155
- }
156
- return Promise.resolve(null);
157
- },
158
- );
286
+ it("should resolve fallback model for prompt_api when browser-built-in and not supported", async () => {
287
+ o.provider = "prompt_api";
288
+ o.model = "browser-built-in";
289
+ mockIsPromptApiSupported.mockReturnValue(false);
290
+ mockGetConfig.mockImplementation(async (_db, key) => {
291
+ if (key === CONFIG_KEYS.PROMPT_API_FALLBACK_MODEL) {
292
+ return "custom-fallback-model";
293
+ }
294
+ return null;
295
+ });
159
296
 
160
- const { buildConversationMessages } =
161
- await import("../../../db/buildConversationMessages.js");
162
- (buildConversationMessages as jest.Mock<any>).mockResolvedValue([
163
- { role: "user", content: "Hi" },
164
- { role: "assistant", content: "Hello" },
165
- ]);
297
+ await compactContext(o, db, "group-1");
298
+
299
+ expect(mockEnsureBuiltinAiPolyfills).toHaveBeenCalled();
300
+ });
166
301
 
167
- o.handleCompactDone = jest.fn<any>().mockResolvedValue(undefined);
302
+ it("should resolve default fallback model when PROMPT_API_FALLBACK_MODEL is null", async () => {
303
+ o.provider = "prompt_api";
304
+ o.model = "";
305
+ mockIsPromptApiSupported.mockReturnValue(false);
306
+ mockGetConfig.mockResolvedValue(null);
168
307
 
169
308
  await compactContext(o, db, "group-1");
170
309
 
171
- expect(o.handleCompactDone).toHaveBeenCalledWith(
172
- db,
173
- "group-1",
174
- "Mock summary from Task API",
175
- );
310
+ expect(mockEnsureBuiltinAiPolyfills).toHaveBeenCalled();
176
311
  });
177
312
 
178
- it("handles effectiveProviderId === 'prompt_api'", async () => {
179
- const { getConfig } = await import("../../../db/getConfig.js");
180
- (getConfig as jest.Mock<any>).mockResolvedValue(null);
313
+ it("should handle pinned provider without pinned model using defaultModel or fallback", async () => {
314
+ await compactContext(o, db, "group-pinned-no-model");
315
+ const postMessage = o.agentWorker?.postMessage as jest.Mock;
316
+ expect(postMessage).toHaveBeenCalled();
317
+ const payload = postMessage.mock.calls[0][0] as {
318
+ payload: { provider: string };
319
+ };
320
+ expect(payload.payload.provider).toBe("openrouter");
321
+
322
+ // Also test group with unknown pinned provider
323
+ o.setState("idle");
324
+ jest
325
+ .spyOn(o, "getApiKeyForSpecificProvider")
326
+ .mockResolvedValue("specific-key");
327
+ await compactContext(o, db, "group-pinned-unknown-prov");
328
+ expect(postMessage).toHaveBeenCalledTimes(2);
329
+ });
181
330
 
182
- o.provider = "prompt_api";
183
- o.providerConfig = { requiresApiKey: false } as any;
184
- o.handleCompactDone = jest.fn<any>().mockResolvedValue(undefined);
331
+ it("should default groupId to DEFAULT_GROUP_ID when omitted", async () => {
332
+ await compactContext(o, db);
333
+ const postMessage = o.agentWorker?.postMessage as jest.Mock;
334
+ expect(postMessage).toHaveBeenCalled();
335
+ const payload = postMessage.mock.calls[0][0] as {
336
+ payload: { groupId: string };
337
+ };
338
+ expect(payload.payload.groupId).toBe(DEFAULT_GROUP_ID);
339
+ });
185
340
 
186
- await compactContext(o, db, "group-1");
341
+ describe("compactionPref === builtin_task_api", () => {
342
+ it("should summarize messages and call handleCompactDone on success", async () => {
343
+ mockGetConfig.mockImplementation(async (_db, key) => {
344
+ if (key === CONFIG_KEYS.COMPACTION_ENGINE_PREFERENCE) {
345
+ return "builtin_task_api";
346
+ }
347
+ return null;
348
+ });
349
+ mockBuildConversationMessages.mockResolvedValueOnce([
350
+ { role: "user", content: "Hello" },
351
+ { role: "assistant", content: { text: "World" } as unknown as string },
352
+ ]);
353
+
354
+ await compactContext(o, db, "group-1");
355
+
356
+ expect(mockSummarizeText).toHaveBeenCalled();
357
+ expect(o.handleCompactDone).toHaveBeenCalledWith(
358
+ db,
359
+ "group-1",
360
+ "Mock task summary",
361
+ );
362
+ });
363
+
364
+ it("should catch error in summarizeText and deliver fallback response", async () => {
365
+ mockGetConfig.mockImplementation(async (_db, key) => {
366
+ if (key === CONFIG_KEYS.COMPACTION_ENGINE_PREFERENCE) {
367
+ return "builtin_task_api";
368
+ }
369
+ return null;
370
+ });
371
+ mockSummarizeText.mockRejectedValueOnce(new Error("Summarizer failed"));
372
+
373
+ await compactContext(o, db, "group-1");
374
+
375
+ expect(mockDeliverResponse).toHaveBeenCalledWith(
376
+ o,
377
+ db,
378
+ "group-1",
379
+ "⚠️ Built-in Task API compaction failed, falling back to provider: Summarizer failed",
380
+ );
381
+ expect(o.agentWorker?.postMessage).toHaveBeenCalled();
382
+ });
383
+
384
+ it("should catch non-Error thrown in summarizeText and deliver fallback response", async () => {
385
+ mockGetConfig.mockImplementation(async (_db, key) => {
386
+ if (key === CONFIG_KEYS.COMPACTION_ENGINE_PREFERENCE) {
387
+ return "builtin_task_api";
388
+ }
389
+ return null;
390
+ });
391
+ mockSummarizeText.mockRejectedValueOnce("Non-error string failure");
392
+
393
+ await compactContext(o, db, "group-1");
394
+
395
+ expect(mockDeliverResponse).toHaveBeenCalledWith(
396
+ o,
397
+ db,
398
+ "group-1",
399
+ "⚠️ Built-in Task API compaction failed, falling back to provider: Non-error string failure",
400
+ );
401
+ });
402
+ });
187
403
 
188
- expect(o.handleCompactDone).toHaveBeenCalledWith(
189
- db,
190
- "group-1",
191
- "Mock prompt API summary",
192
- );
404
+ describe("effectiveProviderId === prompt_api", () => {
405
+ it("should abort if prompt_api remains unsupported after polyfill attempt", async () => {
406
+ o.provider = "prompt_api";
407
+ mockIsPromptApiSupported.mockReturnValue(false);
408
+
409
+ const errorEvents: unknown[] = [];
410
+ const typingEvents: unknown[] = [];
411
+ o.events.on("error", (e) => errorEvents.push(e));
412
+ o.events.on("typing", (e) => typingEvents.push(e));
413
+
414
+ await compactContext(o, db, "group-1");
415
+
416
+ expect(mockEnsureBuiltinAiPolyfills).toHaveBeenCalled();
417
+ expect(errorEvents).toHaveLength(1);
418
+ expect(typingEvents).toContainEqual({
419
+ groupId: "group-1",
420
+ typing: false,
421
+ });
422
+ expect(o.state).toBe("idle");
423
+ });
424
+
425
+ it("should execute compactWithPromptApi, trigger handleWorkerMessage callback, and call handleCompactDone", async () => {
426
+ o.provider = "prompt_api";
427
+ mockIsPromptApiSupported.mockReturnValue(true);
428
+
429
+ await compactContext(o, db, "group-1");
430
+
431
+ expect(mockCompactWithPromptApi).toHaveBeenCalled();
432
+ const onMsgCb = mockCompactWithPromptApi.mock.calls[0][3];
433
+ await onMsgCb({ type: "stream-chunk" });
434
+ expect(mockHandleWorkerMessage).toHaveBeenCalledWith(o, db, {
435
+ type: "stream-chunk",
436
+ });
437
+
438
+ expect(o.handleCompactDone).toHaveBeenCalledWith(
439
+ db,
440
+ "group-1",
441
+ "Mock prompt API summary",
442
+ );
443
+ expect(o.promptControllers.has("group-1")).toBe(false);
444
+ });
445
+
446
+ it("should return early when compactWithPromptApi throws AbortError", async () => {
447
+ o.provider = "prompt_api";
448
+ mockIsPromptApiSupported.mockReturnValue(true);
449
+
450
+ const abortError = new Error("Aborted");
451
+ abortError.name = "AbortError";
452
+ mockCompactWithPromptApi.mockRejectedValueOnce(abortError);
453
+
454
+ await compactContext(o, db, "group-1");
455
+
456
+ expect(mockDeliverResponse).not.toHaveBeenCalled();
457
+ expect(o.promptControllers.has("group-1")).toBe(false);
458
+ });
459
+
460
+ it("should deliver error response when compactWithPromptApi throws regular Error", async () => {
461
+ o.provider = "prompt_api";
462
+ mockIsPromptApiSupported.mockReturnValue(true);
463
+
464
+ mockCompactWithPromptApi.mockRejectedValueOnce(
465
+ new Error("Prompt API crash"),
466
+ );
467
+
468
+ await compactContext(o, db, "group-1");
469
+
470
+ expect(mockDeliverResponse).toHaveBeenCalledWith(
471
+ o,
472
+ db,
473
+ "group-1",
474
+ "⚠️ Error: Compaction failed: Prompt API crash",
475
+ );
476
+ expect(o.promptControllers.has("group-1")).toBe(false);
477
+ });
478
+
479
+ it("should deliver error response when compactWithPromptApi throws non-Error", async () => {
480
+ o.provider = "prompt_api";
481
+ mockIsPromptApiSupported.mockReturnValue(true);
482
+
483
+ mockCompactWithPromptApi.mockRejectedValueOnce("String error thrown");
484
+
485
+ await compactContext(o, db, "group-1");
486
+
487
+ expect(mockDeliverResponse).toHaveBeenCalledWith(
488
+ o,
489
+ db,
490
+ "group-1",
491
+ "⚠️ Error: Compaction failed: String error thrown",
492
+ );
493
+ expect(o.promptControllers.has("group-1")).toBe(false);
494
+ });
193
495
  });
194
496
  });