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
@@ -343,6 +343,259 @@ export function isWebMcpSupported(): boolean {
343
343
  return !!getModelContextApi();
344
344
  }
345
345
 
346
+ /**
347
+ * Event fired when a tool execution begins (PR #245 / Chrome 156+).
348
+ * Dispatched on `document.modelContext` in Chrome 156+, or `window` in Chrome < 156.
349
+ */
350
+ export interface WebMcpToolActivatedEvent extends Event {
351
+ readonly toolName: string;
352
+ }
353
+
354
+ /**
355
+ * Event fired when a tool execution is cancelled (PR #245 / Chrome 156+).
356
+ * Dispatched on `document.modelContext` in Chrome 156+, or `window` in Chrome < 156.
357
+ */
358
+ export interface WebMcpToolCancelEvent extends Event {
359
+ readonly toolName: string;
360
+ }
361
+
362
+ export type WebMcpEventTarget = EventTarget & {
363
+ addEventListener: (type: string, listener: any, options?: any) => void;
364
+ removeEventListener: (type: string, listener: any, options?: any) => void;
365
+ };
366
+
367
+ export interface WebMcpEventListenerOptions extends AddEventListenerOptions {
368
+ /**
369
+ * If true, also attaches a fallback listener to window when modelContext is selected
370
+ * (or vice versa), deduplicating events by toolName within the same tick if both fire.
371
+ */
372
+ listenBoth?: boolean;
373
+ }
374
+
375
+ /**
376
+ * Resolves the appropriate EventTarget for WebMCP lifecycle events
377
+ * (`toolactivated` and `toolcancel`) with backwards compatibility.
378
+ *
379
+ * Starting in Chrome 156.0.8076.0 (PR #245), `ontoolactivated` and `ontoolcancel`
380
+ * event handler properties and lifecycle events moved from `window` to `document.modelContext`.
381
+ * In Chrome < 156, these events were fired on `window`.
382
+ *
383
+ * This function practices robust feature detection and graceful degradation:
384
+ * 1. Safely checks for existence of `document` and `window` to avoid ReferenceErrors
385
+ * in non-DOM / headless / Node.js environments.
386
+ * 2. Checks if `on${type}` is present in `document.modelContext` (or `navigator.modelContext`).
387
+ * If present and it supports `addEventListener`, `modelContext` is returned.
388
+ * 3. Checks if `on${type}` is present in `window`. If present and `window` supports
389
+ * `addEventListener`, `window` is returned (legacy Chrome < 156 path).
390
+ * 4. Fallback: If neither has `on${type}` (e.g. polyfill, synthetic mock, or uninitialized prototype),
391
+ * prefers `modelContext` if it supports `addEventListener`, otherwise falls back to `window`.
392
+ * 5. Returns `null` if no valid EventTarget is found.
393
+ */
394
+ export function getWebMcpEventTarget(
395
+ type?: "toolactivated" | "toolcancel" | string,
396
+ ): WebMcpEventTarget | null {
397
+ const doc = typeof document !== "undefined" ? document : undefined;
398
+ const win = typeof window !== "undefined" ? window : undefined;
399
+ const nav = typeof navigator !== "undefined" ? navigator : undefined;
400
+
401
+ const modelContext: any =
402
+ (doc as any)?.modelContext ?? (nav as any)?.modelContext;
403
+
404
+ const hasModelContextListener =
405
+ modelContext && typeof modelContext.addEventListener === "function";
406
+ const hasWindowListener =
407
+ win && typeof (win as any).addEventListener === "function";
408
+
409
+ if (type) {
410
+ const handlerKey = `on${type}`;
411
+
412
+ // 1. Chrome 156+ path (PR #245): ontoolactivated / ontoolcancel on document.modelContext
413
+ if (modelContext && handlerKey in modelContext && hasModelContextListener) {
414
+ return modelContext as WebMcpEventTarget;
415
+ }
416
+
417
+ // 2. Chrome < 156 legacy path: ontoolactivated / ontoolcancel on window
418
+ if (win && handlerKey in win && hasWindowListener) {
419
+ return win as WebMcpEventTarget;
420
+ }
421
+ }
422
+
423
+ // 3. Fallback when neither target explicitly reflects on${type}
424
+ if (hasModelContextListener) {
425
+ return modelContext as WebMcpEventTarget;
426
+ }
427
+
428
+ if (hasWindowListener) {
429
+ return win as WebMcpEventTarget;
430
+ }
431
+
432
+ return null;
433
+ }
434
+
435
+ /**
436
+ * Safely registers a listener for WebMCP lifecycle events (`toolactivated`, `toolcancel`)
437
+ * with graceful degradation, event payload normalization, and automatic unsubscription.
438
+ *
439
+ * Returns an idempotent unsubscribe cleanup function.
440
+ */
441
+ export function addWebMcpEventListener<
442
+ T extends "toolactivated" | "toolcancel" | string =
443
+ | "toolactivated"
444
+ | "toolcancel",
445
+ >(
446
+ type: T,
447
+ listener: (
448
+ event: T extends "toolactivated"
449
+ ? WebMcpToolActivatedEvent
450
+ : T extends "toolcancel"
451
+ ? WebMcpToolCancelEvent
452
+ : Event & { toolName?: string },
453
+ ) => void,
454
+ options?: boolean | WebMcpEventListenerOptions,
455
+ ): () => void {
456
+ const target = getWebMcpEventTarget(type);
457
+ const opts = typeof options === "object" ? options : undefined;
458
+ const listenBoth = opts?.listenBoth ?? false;
459
+
460
+ const win =
461
+ typeof window !== "undefined" &&
462
+ typeof (window as any).addEventListener === "function"
463
+ ? (window as unknown as WebMcpEventTarget)
464
+ : null;
465
+ const doc = typeof document !== "undefined" ? document : null;
466
+ const nav = typeof navigator !== "undefined" ? navigator : null;
467
+ const modelContext: any =
468
+ (doc as any)?.modelContext ?? (nav as any)?.modelContext;
469
+ const hasMc =
470
+ modelContext && typeof modelContext.addEventListener === "function";
471
+
472
+ const secondaryTarget: WebMcpEventTarget | null = listenBoth
473
+ ? target === modelContext
474
+ ? win !== target
475
+ ? win
476
+ : null
477
+ : hasMc
478
+ ? (modelContext as WebMcpEventTarget)
479
+ : null
480
+ : null;
481
+
482
+ if (!target && !secondaryTarget) {
483
+ return () => {};
484
+ }
485
+
486
+ let recentToolNames: Set<string> | null = null;
487
+ let clearTimer: ReturnType<typeof setTimeout> | null = null;
488
+
489
+ const wrapHandler = (evt: Event) => {
490
+ let toolName = (evt as any).toolName;
491
+ if (
492
+ typeof toolName !== "string" &&
493
+ "detail" in evt &&
494
+ typeof (evt as any).detail === "object"
495
+ ) {
496
+ toolName = (evt as any).detail?.toolName;
497
+ }
498
+ if (typeof toolName !== "string") {
499
+ toolName = "";
500
+ }
501
+
502
+ if (!("toolName" in evt) || (evt as any).toolName !== toolName) {
503
+ try {
504
+ Object.defineProperty(evt, "toolName", {
505
+ value: toolName,
506
+ configurable: true,
507
+ enumerable: true,
508
+ });
509
+ } catch {
510
+ // Ignore errors if event is sealed
511
+ }
512
+ }
513
+
514
+ if (listenBoth && toolName) {
515
+ if (!recentToolNames) {
516
+ recentToolNames = new Set();
517
+ }
518
+ if (recentToolNames.has(toolName)) {
519
+ return;
520
+ }
521
+ recentToolNames.add(toolName);
522
+ if (!clearTimer) {
523
+ clearTimer = setTimeout(() => {
524
+ recentToolNames?.clear();
525
+ clearTimer = null;
526
+ }, 100);
527
+ }
528
+ }
529
+
530
+ listener(evt as any);
531
+ };
532
+
533
+ if (target) {
534
+ target.addEventListener(type, wrapHandler as EventListener, options);
535
+ }
536
+ if (secondaryTarget) {
537
+ secondaryTarget.addEventListener(
538
+ type,
539
+ wrapHandler as EventListener,
540
+ options,
541
+ );
542
+ }
543
+
544
+ let unsubscribed = false;
545
+ const unsubscribe = () => {
546
+ if (unsubscribed) {
547
+ return;
548
+ }
549
+ unsubscribed = true;
550
+ if (clearTimer) {
551
+ clearTimeout(clearTimer);
552
+ clearTimer = null;
553
+ }
554
+ if (target) {
555
+ target.removeEventListener(type, wrapHandler as EventListener, options);
556
+ }
557
+ if (secondaryTarget) {
558
+ secondaryTarget.removeEventListener(
559
+ type,
560
+ wrapHandler as EventListener,
561
+ options,
562
+ );
563
+ }
564
+ };
565
+
566
+ if (opts?.signal) {
567
+ if (opts.signal.aborted) {
568
+ unsubscribe();
569
+ return () => {};
570
+ }
571
+ opts.signal.addEventListener("abort", () => unsubscribe(), { once: true });
572
+ }
573
+
574
+ return unsubscribe;
575
+ }
576
+
577
+ /**
578
+ * Convenience helper to subscribe to tool execution start events (`toolactivated`).
579
+ * Returns an unsubscribe cleanup function.
580
+ */
581
+ export function onWebMcpToolActivated(
582
+ listener: (event: WebMcpToolActivatedEvent) => void,
583
+ options?: boolean | WebMcpEventListenerOptions,
584
+ ): () => void {
585
+ return addWebMcpEventListener("toolactivated", listener, options);
586
+ }
587
+
588
+ /**
589
+ * Convenience helper to subscribe to tool execution cancel events (`toolcancel`).
590
+ * Returns an unsubscribe cleanup function.
591
+ */
592
+ export function onWebMcpToolCancel(
593
+ listener: (event: WebMcpToolCancelEvent) => void,
594
+ options?: boolean | WebMcpEventListenerOptions,
595
+ ): () => void {
596
+ return addWebMcpEventListener("toolcancel", listener, options);
597
+ }
598
+
346
599
  /**
347
600
  * Consequential tools that perform high-stakes, irreversible, or external real-world actions
348
601
  * (such as deleting data, executing arbitrary system commands, or transmitting live communications).
@@ -1,7 +1,11 @@
1
- import { describe, it, expect } from "@jest/globals";
1
+ import { describe, it, expect, afterEach } from "@jest/globals";
2
2
  import { nativeBashExecutor } from "./native-bash-executor.js";
3
3
 
4
4
  describe("nativeBashExecutor", () => {
5
+ afterEach(() => {
6
+ delete (globalThis as Record<string, unknown>).__getStorageRootPath;
7
+ });
8
+
5
9
  it("executes a shell command and captures stdout", async () => {
6
10
  const output = await nativeBashExecutor({
7
11
  command: "echo 'hello from native bash'",
@@ -26,4 +30,30 @@ describe("nativeBashExecutor", () => {
26
30
  });
27
31
  expect(output).toContain("test stderr output");
28
32
  });
33
+
34
+ it("captures both stdout and stderr formatted with newline", async () => {
35
+ const output = await nativeBashExecutor({
36
+ command: 'node -e \'process.stdout.write("out"); console.error("err");\'',
37
+ timeoutSec: 5,
38
+ });
39
+ expect(output).toContain("out\nerr");
40
+ });
41
+
42
+ it("falls back to error message when command fails with empty output", async () => {
43
+ const output = await nativeBashExecutor({
44
+ command: "node -e 'process.exit(42)'",
45
+ timeoutSec: 5,
46
+ });
47
+ expect(output).toContain("Command failed");
48
+ });
49
+
50
+ it("uses __getStorageRootPath when provided on globalThis", async () => {
51
+ (globalThis as Record<string, unknown>).__getStorageRootPath = () =>
52
+ process.cwd();
53
+ const output = await nativeBashExecutor({
54
+ command: "pwd",
55
+ timeoutSec: 5,
56
+ });
57
+ expect(output.trim()).toBe(process.cwd());
58
+ });
29
59
  });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { extractConflictPaths } from "./extractConflictPaths.js";
3
+
4
+ describe("extractConflictPaths", () => {
5
+ it("extracts multiple file paths from conflict message", () => {
6
+ const message =
7
+ "Automatic merge failed; conflicts in the following files: fileA.ts, fileB.ts, fileC.ts";
8
+ expect(extractConflictPaths(message)).toEqual([
9
+ "fileA.ts",
10
+ "fileB.ts",
11
+ "fileC.ts",
12
+ ]);
13
+ });
14
+
15
+ it("extracts single file path from singular conflict message", () => {
16
+ const message = "conflict in the following file: readme.md";
17
+ expect(extractConflictPaths(message)).toEqual(["readme.md"]);
18
+ });
19
+
20
+ it("returns empty array when message does not match conflict format", () => {
21
+ const message = "Merge successful with fast-forward";
22
+ expect(extractConflictPaths(message)).toEqual([]);
23
+ });
24
+ });
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { parseConflictRegions } from "./parseConflictRegions.js";
3
+
4
+ describe("parseConflictRegions", () => {
5
+ it("parses single standard conflict region", () => {
6
+ const content = [
7
+ "before",
8
+ "<<<<<<< HEAD",
9
+ "our content line 1",
10
+ "our content line 2",
11
+ "=======",
12
+ "their content",
13
+ ">>>>>>> branch-feat",
14
+ "after",
15
+ ].join("\n");
16
+
17
+ const regions = parseConflictRegions(content);
18
+ expect(regions).toHaveLength(1);
19
+ expect(regions[0]).toEqual({
20
+ ours: "our content line 1\nour content line 2",
21
+ oursLabel: "HEAD",
22
+ startLine: 2,
23
+ theirs: "their content",
24
+ theirsLabel: "branch-feat",
25
+ });
26
+ });
27
+
28
+ it("handles unclosed conflict regions that reach end-of-file without closing marker", () => {
29
+ const content = [
30
+ "<<<<<<< HEAD",
31
+ "our changes",
32
+ "=======",
33
+ "their unclosed changes",
34
+ ].join("\n");
35
+
36
+ const regions = parseConflictRegions(content);
37
+ expect(regions).toHaveLength(1);
38
+ expect(regions[0].theirsLabel).toBe("");
39
+ expect(regions[0].theirs).toBe("their unclosed changes");
40
+ });
41
+
42
+ it("returns empty array when no conflict markers are present", () => {
43
+ const content = "regular file content\nwithout conflicts";
44
+ expect(parseConflictRegions(content)).toEqual([]);
45
+ });
46
+ });
@@ -1,27 +1,29 @@
1
1
  import { describe, it, expect, beforeEach } from "@jest/globals";
2
2
  import { resolveCorsProxy } from "./resolveCorsProxy.js";
3
3
  import { setHeadlessMode } from "../../../../config/headless.js";
4
+ import type { ShadowClawDatabase } from "../../../../db/types.js";
5
+ import type { GitToolDeps } from "../../../../subsystems/git/types.js";
4
6
 
5
7
  describe("resolveCorsProxy", () => {
6
- let mockDb: any;
7
- let mockDeps: any;
8
+ const mockDb = {} as ShadowClawDatabase;
9
+ let mockDeps: GitToolDeps;
8
10
  let configStore: Record<string, string>;
9
11
 
10
12
  beforeEach(() => {
11
13
  configStore = {};
12
- mockDb = {};
13
14
  mockDeps = {
14
15
  configKeys: {
15
16
  GIT_CORS_PROXY: "git_cors_proxy",
16
17
  GIT_PROXY_URL: "git_proxy_url",
17
18
  },
18
- getConfig: async (_db: any, key: string) => configStore[key],
19
+ getConfig: async (_db: ShadowClawDatabase, key: string) =>
20
+ configStore[key],
19
21
  getProxyUrl: (pref: string, custom?: string) => {
20
22
  if (pref === "custom") return custom || "https://custom.proxy";
21
23
  if (pref === "public") return "https://public.proxy";
22
24
  return "http://127.0.0.1:8888/git-proxy";
23
25
  },
24
- };
26
+ } as unknown as GitToolDeps;
25
27
  });
26
28
 
27
29
  it("returns local proxy URL in browser mode by default", async () => {
@@ -44,10 +46,29 @@ describe("resolveCorsProxy", () => {
44
46
  expect(result).toBe("https://my-proxy.internal");
45
47
  });
46
48
 
49
+ it("falls back when custom proxy is selected but no custom URL is provided", async () => {
50
+ setHeadlessMode(false);
51
+ configStore["git_cors_proxy"] = "custom";
52
+ configStore["git_proxy_url"] = "";
53
+ const result = await resolveCorsProxy(mockDb, mockDeps);
54
+ expect(result).toBe("http://127.0.0.1:8888/git-proxy");
55
+ });
56
+
47
57
  it("returns public proxy URL when configured in browser mode", async () => {
48
58
  setHeadlessMode(false);
49
59
  configStore["git_cors_proxy"] = "public";
50
60
  const result = await resolveCorsProxy(mockDb, mockDeps);
51
61
  expect(result).toBe("https://public.proxy");
52
62
  });
63
+
64
+ it("returns undefined when pref is direct or none", async () => {
65
+ setHeadlessMode(false);
66
+ configStore["git_cors_proxy"] = "direct";
67
+ const directResult = await resolveCorsProxy(mockDb, mockDeps);
68
+ expect(directResult).toBeUndefined();
69
+
70
+ configStore["git_cors_proxy"] = "none";
71
+ const noneResult = await resolveCorsProxy(mockDb, mockDeps);
72
+ expect(noneResult).toBeUndefined();
73
+ });
53
74
  });
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { truncateSnippet } from "./truncateSnippet.js";
3
+
4
+ describe("truncateSnippet", () => {
5
+ it("returns unchanged text when line count is less than or equal to maxLines", () => {
6
+ const text = "line 1\nline 2\nline 3";
7
+ expect(truncateSnippet(text, 5)).toBe(text);
8
+ expect(truncateSnippet(text, 3)).toBe(text);
9
+ });
10
+
11
+ it("truncates text and appends remaining line count when line count exceeds maxLines", () => {
12
+ const text = "line 1\nline 2\nline 3\nline 4\nline 5";
13
+ const result = truncateSnippet(text, 2);
14
+ expect(result).toBe("line 1\nline 2\n [... 3 more lines]");
15
+ });
16
+ });
@@ -1,21 +1,41 @@
1
1
  /** @jest-environment node */
2
2
  import { describe, it, expect, jest, beforeEach } from "@jest/globals";
3
- import {
4
- executeNodeTransformersCompletion,
5
- setTransformersServiceForTests,
6
- } from "./node-transformers-executor.js";
3
+ import type { TransformersRuntimeService } from "../../server/services/transformers-runtime.js";
4
+
5
+ const mockCreateServiceInstance: TransformersRuntimeService = {
6
+ runChatCompletion: jest.fn(async () => ({
7
+ text: "Created service default output",
8
+ promptTokens: 5,
9
+ completionTokens: 5,
10
+ })),
11
+ } as unknown as TransformersRuntimeService;
12
+
13
+ const mockCreateTransformersRuntimeService = jest.fn(
14
+ () => mockCreateServiceInstance,
15
+ );
16
+
17
+ jest.unstable_mockModule(
18
+ "../../server/services/transformers-runtime.js",
19
+ () => ({
20
+ createTransformersRuntimeService: mockCreateTransformersRuntimeService,
21
+ }),
22
+ );
23
+
24
+ const { executeNodeTransformersCompletion, setTransformersServiceForTests } =
25
+ await import("./node-transformers-executor.js");
7
26
 
8
27
  describe("executeNodeTransformersCompletion", () => {
9
- let mockService: any;
28
+ let mockService: TransformersRuntimeService;
10
29
 
11
30
  beforeEach(() => {
31
+ jest.clearAllMocks();
12
32
  mockService = {
13
33
  runChatCompletion: jest.fn(async () => ({
14
34
  text: "Hello from local ONNX!",
15
35
  promptTokens: 12,
16
36
  completionTokens: 8,
17
37
  })),
18
- };
38
+ } as unknown as TransformersRuntimeService;
19
39
  setTransformersServiceForTests(mockService);
20
40
  });
21
41
 
@@ -55,4 +75,83 @@ describe("executeNodeTransformersCompletion", () => {
55
75
  }),
56
76
  );
57
77
  });
78
+
79
+ it("handles tool calls returned by the model output", async () => {
80
+ mockService.runChatCompletion = jest.fn(async () => ({
81
+ text: 'call:read_file{"path":"test.txt"}',
82
+ promptTokens: 20,
83
+ completionTokens: 15,
84
+ })) as typeof mockService.runChatCompletion;
85
+
86
+ const tools = [
87
+ {
88
+ name: "read_file",
89
+ description: "Read a file",
90
+ parameters: {
91
+ type: "object",
92
+ properties: { path: { type: "string" } },
93
+ },
94
+ },
95
+ ];
96
+
97
+ const response = await executeNodeTransformersCompletion({
98
+ modelId: "onnx-community/gemma-4-E2B-it-ONNX",
99
+ messages: [{ role: "user", content: "Read test.txt" }],
100
+ tools,
101
+ });
102
+
103
+ expect(mockService.runChatCompletion).toHaveBeenCalledWith(
104
+ expect.objectContaining({
105
+ tools: expect.arrayContaining([
106
+ expect.objectContaining({
107
+ type: "function",
108
+ function: expect.objectContaining({ name: "read_file" }),
109
+ }),
110
+ ]),
111
+ }),
112
+ );
113
+
114
+ expect(response.choices[0].finish_reason).toBe("tool_calls");
115
+ expect(response.choices[0].message.content).toBeNull();
116
+ expect(response.choices[0].message.tool_calls).toHaveLength(1);
117
+ expect(response.choices[0].message.tool_calls[0].function.name).toBe(
118
+ "read_file",
119
+ );
120
+ expect(response.choices[0].message.tool_calls[0].function.arguments).toBe(
121
+ JSON.stringify({ path: "test.txt" }),
122
+ );
123
+ });
124
+
125
+ it("creates default service when defaultService is null", async () => {
126
+ setTransformersServiceForTests(null);
127
+
128
+ const response = await executeNodeTransformersCompletion({
129
+ modelId: "onnx-community/Qwen3-0.6B-ONNX",
130
+ messages: [{ role: "user", content: "Test default" }],
131
+ });
132
+
133
+ expect(mockCreateTransformersRuntimeService).toHaveBeenCalled();
134
+ expect(response.choices[0].message.content).toBe(
135
+ "Created service default output",
136
+ );
137
+ });
138
+
139
+ it("uses custom injected service when provided in options", async () => {
140
+ const customService: TransformersRuntimeService = {
141
+ runChatCompletion: jest.fn(async () => ({
142
+ text: "Custom service response",
143
+ promptTokens: 3,
144
+ completionTokens: 4,
145
+ })),
146
+ } as unknown as TransformersRuntimeService;
147
+
148
+ const response = await executeNodeTransformersCompletion({
149
+ modelId: "onnx-community/Qwen3-0.6B-ONNX",
150
+ messages: [{ role: "user", content: "Custom" }],
151
+ service: customService,
152
+ });
153
+
154
+ expect(customService.runChatCompletion).toHaveBeenCalled();
155
+ expect(response.choices[0].message.content).toBe("Custom service response");
156
+ });
58
157
  });