shadow-claw 1.26.0 → 1.27.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 (230) hide show
  1. package/.agents/skills/main/skill-creator/SKILL.md +39 -0
  2. package/.agents/skills/main/skill-creator/references/declarative-pipelines.md +44 -0
  3. package/.agents/skills/main/skill-creator/references/declarative-tools.md +35 -0
  4. package/.agents/skills/main/skill-creator/references/frontmatter-spec.md +50 -0
  5. package/.well-known/agent-skills/index.json +14 -0
  6. package/README.md +5 -3
  7. package/bin/build/build.mjs +146 -15
  8. package/bin/build/build.test.mjs +57 -0
  9. package/bin/cli.mjs +26 -0
  10. package/bin/commands/mcp.mjs +277 -55
  11. package/bin/commands/mcp.test.mjs +275 -1
  12. package/bin/commands/skills-index.mjs +328 -0
  13. package/bin/commands/skills-index.test.mjs +161 -0
  14. package/bin/site-config/utils/custom-elements/get-approved-custom-element-scripts.test.mjs +2 -0
  15. package/bin/site-config/utils/custom-elements/sanitize-embedded-custom-element-scripts.test.mjs +10 -1
  16. package/dist/lib/components/index.js +25 -25
  17. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  18. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  19. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  20. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  21. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  22. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  23. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  24. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  25. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  26. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  27. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  28. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  29. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  30. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  31. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  32. package/dist/lib/components/shadow-claw-channels.js +1 -1
  33. package/dist/lib/components/shadow-claw-chat.js +1 -1
  34. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  35. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  36. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  37. package/dist/lib/components/shadow-claw-files.js +1 -1
  38. package/dist/lib/components/shadow-claw-git.js +1 -1
  39. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  40. package/dist/lib/components/shadow-claw-llm.js +1 -1
  41. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  42. package/dist/lib/components/shadow-claw-networking.js +1 -1
  43. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  44. package/dist/lib/components/shadow-claw-pages.js +1 -1
  45. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  46. package/dist/lib/components/shadow-claw-settings.js +1 -1
  47. package/dist/lib/components/shadow-claw-storage.js +1 -1
  48. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  49. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  50. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  51. package/dist/lib/components/shadow-claw-tools.js +1 -1
  52. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  53. package/dist/lib/components/shadow-claw.js +1 -1
  54. package/dist/lib/{configurePeerJs-CrlGan0m.js → configurePeerJs-oODzltaC.js} +2 -2
  55. package/dist/lib/{configurePeerJs-CrlGan0m.js.map → configurePeerJs-oODzltaC.js.map} +1 -1
  56. package/dist/lib/core/control-plane-client.d.ts +1 -0
  57. package/dist/lib/{downloadGroupFile-DboUjmp2.js → downloadGroupFile-DmcBQU1n.js} +2 -2
  58. package/dist/lib/{downloadGroupFile-DboUjmp2.js.map → downloadGroupFile-DmcBQU1n.js.map} +1 -1
  59. package/dist/lib/{file-viewer-BrK2Wxgh.js → file-viewer-C3YKnq_-.js} +4 -3
  60. package/dist/lib/{file-viewer-BrK2Wxgh.js.map → file-viewer-C3YKnq_-.js.map} +1 -1
  61. package/dist/lib/index.js +26 -26
  62. package/dist/lib/{initControlPlane-CsO4UR6_.js → initControlPlane-C2zURVIS.js} +86 -13
  63. package/dist/lib/initControlPlane-C2zURVIS.js.map +1 -0
  64. package/dist/lib/{markdown-eeSTFnMA.js → markdown-C6EHl9zs.js} +3 -3
  65. package/dist/lib/{markdown-eeSTFnMA.js.map → markdown-C6EHl9zs.js.map} +1 -1
  66. package/dist/lib/{orchestrator-CJvoC2Uu.js → orchestrator-CACLa_Ro.js} +7 -6
  67. package/dist/lib/{orchestrator-CJvoC2Uu.js.map → orchestrator-CACLa_Ro.js.map} +1 -1
  68. package/dist/lib/{orchestrator-DCfomsn9.js → orchestrator-KRjx0eba.js} +6 -534
  69. package/dist/lib/{orchestrator-DCfomsn9.js.map → orchestrator-KRjx0eba.js.map} +1 -1
  70. package/dist/lib/parsePath-DwC_YZjw.js +16 -0
  71. package/dist/lib/parsePath-DwC_YZjw.js.map +1 -0
  72. package/dist/lib/{peerjs-Dp02bXcG.js → peerjs-DFWOBcXf.js} +4 -4
  73. package/dist/lib/{peerjs-Dp02bXcG.js.map → peerjs-DFWOBcXf.js.map} +1 -1
  74. package/dist/lib/{readGroupFileBytes-Bve-e5e_.js → readGroupFileBytes-C8oOe8Iu.js} +3 -16
  75. package/dist/lib/readGroupFileBytes-C8oOe8Iu.js.map +1 -0
  76. package/dist/lib/security/custom-element-security.d.ts +8 -0
  77. package/dist/lib/server/control-plane-types.d.ts +1 -1
  78. package/dist/lib/{shadow-claw-D8xINM3c.js → shadow-claw-6UHdiy4a.js} +5 -5
  79. package/dist/lib/{shadow-claw-D8xINM3c.js.map → shadow-claw-6UHdiy4a.js.map} +1 -1
  80. package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js → shadow-claw-a2ui-CpZUTzlh.js} +5 -4
  81. package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js.map → shadow-claw-a2ui-CpZUTzlh.js.map} +1 -1
  82. package/dist/lib/{shadow-claw-accounts-CdruwN8A.js → shadow-claw-accounts-03oJ__XC.js} +2 -2
  83. package/dist/lib/{shadow-claw-accounts-CdruwN8A.js.map → shadow-claw-accounts-03oJ__XC.js.map} +1 -1
  84. package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js → shadow-claw-channel-config-DmfwJc6G.js} +3 -3
  85. package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js.map → shadow-claw-channel-config-DmfwJc6G.js.map} +1 -1
  86. package/dist/lib/{shadow-claw-channels-C_jI4KhP.js → shadow-claw-channels-1N6Jl-By.js} +3 -3
  87. package/dist/lib/{shadow-claw-channels-C_jI4KhP.js.map → shadow-claw-channels-1N6Jl-By.js.map} +1 -1
  88. package/dist/lib/{shadow-claw-chat-B16MspoP.js → shadow-claw-chat-BMoqQJRK.js} +10 -10
  89. package/dist/lib/{shadow-claw-chat-B16MspoP.js.map → shadow-claw-chat-BMoqQJRK.js.map} +1 -1
  90. package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js → shadow-claw-control-plane-UG0o7pCz.js} +6 -6
  91. package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js.map → shadow-claw-control-plane-UG0o7pCz.js.map} +1 -1
  92. package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js → shadow-claw-conversations-BwXep3iu.js} +4 -3
  93. package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js.map → shadow-claw-conversations-BwXep3iu.js.map} +1 -1
  94. package/dist/lib/{shadow-claw-file-viewer-BfDHHgpA.js → shadow-claw-file-viewer-DUxzVxDT.js} +14 -10
  95. package/dist/lib/shadow-claw-file-viewer-DUxzVxDT.js.map +1 -0
  96. package/dist/lib/{shadow-claw-files-EYhOPYIH.js → shadow-claw-files-DIREEnT2.js} +7 -7
  97. package/dist/lib/{shadow-claw-files-EYhOPYIH.js.map → shadow-claw-files-DIREEnT2.js.map} +1 -1
  98. package/dist/lib/{shadow-claw-git-tcSpT-P0.js → shadow-claw-git-Bg1SWgsF.js} +4 -4
  99. package/dist/lib/{shadow-claw-git-tcSpT-P0.js.map → shadow-claw-git-Bg1SWgsF.js.map} +1 -1
  100. package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js → shadow-claw-integrations-Ds-CLPhj.js} +2 -2
  101. package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js.map → shadow-claw-integrations-Ds-CLPhj.js.map} +1 -1
  102. package/dist/lib/{shadow-claw-llm-BazJdD5f.js → shadow-claw-llm-CazBrVpM.js} +3 -3
  103. package/dist/lib/{shadow-claw-llm-BazJdD5f.js.map → shadow-claw-llm-CazBrVpM.js.map} +1 -1
  104. package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js → shadow-claw-mcp-remote-CT5Q-54E.js} +2 -2
  105. package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js.map → shadow-claw-mcp-remote-CT5Q-54E.js.map} +1 -1
  106. package/dist/lib/{shadow-claw-networking-DImPxe0H.js → shadow-claw-networking-Df85tCbO.js} +2 -2
  107. package/dist/lib/{shadow-claw-networking-DImPxe0H.js.map → shadow-claw-networking-Df85tCbO.js.map} +1 -1
  108. package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js → shadow-claw-page-header-Cti2fzf-.js} +2 -2
  109. package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js.map → shadow-claw-page-header-Cti2fzf-.js.map} +1 -1
  110. package/dist/lib/{shadow-claw-pages-D54jF1Qq.js → shadow-claw-pages-BgPyIEZO.js} +15 -11
  111. package/dist/lib/shadow-claw-pages-BgPyIEZO.js.map +1 -0
  112. package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js → shadow-claw-peerjs-xkBXILed.js} +3 -3
  113. package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js.map → shadow-claw-peerjs-xkBXILed.js.map} +1 -1
  114. package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js → shadow-claw-settings-CN89xRVU.js} +4 -4
  115. package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js.map → shadow-claw-settings-CN89xRVU.js.map} +1 -1
  116. package/dist/lib/{shadow-claw-storage-CFOjGf1d.js → shadow-claw-storage-VnhcObCe.js} +3 -3
  117. package/dist/lib/{shadow-claw-storage-CFOjGf1d.js.map → shadow-claw-storage-VnhcObCe.js.map} +1 -1
  118. package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js → shadow-claw-task-server-jgEC2hHB.js} +2 -2
  119. package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js.map → shadow-claw-task-server-jgEC2hHB.js.map} +1 -1
  120. package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js → shadow-claw-tasks-i_evIufy.js} +6 -6
  121. package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js.map → shadow-claw-tasks-i_evIufy.js.map} +1 -1
  122. package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js → shadow-claw-terminal-CwScROlu.js} +2 -2
  123. package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js.map → shadow-claw-terminal-CwScROlu.js.map} +1 -1
  124. package/dist/lib/{shadow-claw-tools-DxXIm0ku.js → shadow-claw-tools-DD3fyXP4.js} +5 -4
  125. package/dist/lib/{shadow-claw-tools-DxXIm0ku.js.map → shadow-claw-tools-DD3fyXP4.js.map} +1 -1
  126. package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js → shadow-claw-webvm-DbJq4I3-.js} +2 -2
  127. package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js.map → shadow-claw-webvm-DbJq4I3-.js.map} +1 -1
  128. package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js → syncWebMcpRegistration-DNLnQ-jz.js} +3 -2
  129. package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js.map → syncWebMcpRegistration-DNLnQ-jz.js.map} +1 -1
  130. package/dist/lib/tools-DwwB9RBa.js +546 -0
  131. package/dist/lib/tools-DwwB9RBa.js.map +1 -0
  132. package/dist/lib/{trusted-types-CkLDpLiZ.js → trusted-types-Dx0RZlrd.js} +19 -8
  133. package/dist/lib/trusted-types-Dx0RZlrd.js.map +1 -0
  134. package/dist/lib/utils/index.js +1 -1
  135. package/dist/lib/utils/utils.js +1 -1
  136. package/dist/public/.well-known/agent-skills/index.json +14 -0
  137. package/dist/public/AGENTS.md +5 -3
  138. package/dist/public/README.md +5 -3
  139. package/dist/public/agent.worker.js +1 -1
  140. package/dist/public/{configurePeerJs-COcwE0E7.js → configurePeerJs-BqgeBjP7.js} +1 -1
  141. package/dist/public/custom-element-security-FU04Cq05.js +1 -0
  142. package/dist/public/dist-B9m1QM9v.js +58 -0
  143. package/dist/public/docs/README.md +31 -31
  144. package/dist/public/docs/example/article/index.html +1 -1
  145. package/dist/public/docs/publishing/index.html +1 -1
  146. package/dist/public/docs/skill-creator/index.html +1 -1
  147. package/dist/public/docs/subsystems/a2ui.md +11 -1
  148. package/dist/public/docs/subsystems/cli.md +29 -0
  149. package/dist/public/docs/subsystems/control-plane.md +8 -2
  150. package/dist/public/docs/subsystems/custom-element-security.md +19 -9
  151. package/dist/public/docs/subsystems/mcp-server.md +39 -15
  152. package/dist/public/docs/subsystems/skills.md +30 -0
  153. package/dist/public/docs/subsystems/webmcp.md +9 -0
  154. package/dist/public/downloadGroupFile-D8KRJGGL.js +1 -0
  155. package/dist/public/e2e/README.md +7 -4
  156. package/dist/public/{e2e-bridge-CHARpbpW.js → e2e-bridge-Ceqr5kGc.js} +1 -1
  157. package/dist/public/effect-BJCrpFdp.js +1 -0
  158. package/dist/public/file-viewer-C-6Jc_tb.js +1 -0
  159. package/dist/public/index.html +1 -1
  160. package/dist/public/index.js +1 -1
  161. package/dist/public/initControlPlane-BN9cWyO0.js +1 -0
  162. package/dist/public/main/index.html +1 -1
  163. package/dist/public/main/memory/index.html +1 -1
  164. package/dist/public/{markdown-1I3O6Mmd.js → markdown-C3M4OW_A.js} +1 -1
  165. package/dist/public/{orchestrator-Cl95w2vb.js → orchestrator-CMbCTjBf.js} +7 -7
  166. package/dist/public/{orchestrator-D-xlXG92.js → orchestrator-DHjdLtqa.js} +663 -663
  167. package/dist/public/peerjs-DUCKH-m9.js +1 -0
  168. package/dist/public/service-worker.js +1 -1
  169. package/dist/public/{shadow-claw-CaoKmIIB.js → shadow-claw-EAt2ml5Y.js} +3 -3
  170. package/dist/public/{shadow-claw-accounts-CJvYC4li.js → shadow-claw-accounts-xUOLBsQZ.js} +1 -1
  171. package/dist/public/{shadow-claw-channels-3K62sU5f.js → shadow-claw-channels-XsfuWFF8.js} +4 -4
  172. package/dist/public/{shadow-claw-chat-Cu2keU2I.js → shadow-claw-chat-CbSaOZ4q.js} +7 -7
  173. package/dist/public/{shadow-claw-control-plane-D1OwlQI_.js → shadow-claw-control-plane-DHvN3Ukg.js} +2 -2
  174. package/dist/public/{shadow-claw-conversations-DDtg3BEZ.js → shadow-claw-conversations-BWs5VVfs.js} +2 -2
  175. package/dist/public/{shadow-claw-file-viewer-BR__1z9S.js → shadow-claw-file-viewer-BWsoW5QS.js} +5 -4
  176. package/dist/public/{shadow-claw-files-BJSyZ-Bl.js → shadow-claw-files-CG7MqWiV.js} +8 -8
  177. package/dist/public/{shadow-claw-git-MAjhXRLc.js → shadow-claw-git-DBRPZNav.js} +6 -6
  178. package/dist/public/{shadow-claw-integrations-82UoFuDR.js → shadow-claw-integrations-BBVnO0Fc.js} +8 -8
  179. package/dist/public/{shadow-claw-llm-R8j0lwPP.js → shadow-claw-llm-DMvLgKXo.js} +2 -2
  180. package/dist/public/{shadow-claw-mcp-remote-BzZoz4LX.js → shadow-claw-mcp-remote-Bkx0gsmF.js} +2 -2
  181. package/dist/public/{shadow-claw-networking-DLEd9cNm.js → shadow-claw-networking-Dd8Rw31D.js} +2 -2
  182. package/dist/public/{shadow-claw-notifications-1vwR7TbT.js → shadow-claw-notifications-Dbevj-lq.js} +2 -2
  183. package/dist/public/{shadow-claw-page-header-DMKbkv3s.js → shadow-claw-page-header-BccSHItx.js} +1 -1
  184. package/dist/public/{shadow-claw-pages-CjyeIV__.js → shadow-claw-pages-3_lQAv0t.js} +4 -3
  185. package/dist/public/{shadow-claw-settings-BfbT4dBU.js → shadow-claw-settings-t8TIlgrM.js} +5 -5
  186. package/dist/public/{shadow-claw-storage-C-mWQQML.js → shadow-claw-storage-CfEXoSIE.js} +2 -2
  187. package/dist/public/{shadow-claw-task-server-D3lHp0Nc.js → shadow-claw-task-server-DxQSsgRZ.js} +2 -2
  188. package/dist/public/{shadow-claw-tasks-DVVWEm-c.js → shadow-claw-tasks-DNpicQwz.js} +14 -14
  189. package/dist/public/{shadow-claw-terminal-BwuTRp-O.js → shadow-claw-terminal-DpY6zU_r.js} +1 -1
  190. package/dist/public/{shadow-claw-toast-ClCwgzFG.js → shadow-claw-toast-BkrQIP-3.js} +1 -1
  191. package/dist/public/{shadow-claw-tools-zFLk2nbV.js → shadow-claw-tools-WSwWohnk.js} +2 -2
  192. package/dist/public/{shadow-claw-webvm-qR9X7fg7.js → shadow-claw-webvm-CP6oFjg5.js} +2 -2
  193. package/dist/public/syncWebMcpRegistration-DOn9gLnD.js +1 -0
  194. package/dist/public/toast-BXgUfbyh.js +1 -0
  195. package/dist/public/tools-DvckJZG2.js +1 -0
  196. package/dist/server.js +188 -188
  197. package/package.json +3 -1
  198. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.stories.ts +1380 -93
  199. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.ts +11 -6
  200. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +8 -1
  201. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +13 -5
  202. package/src/components/shadow-claw-pages/utils/buildHtmlPageSrcdoc.ts +13 -5
  203. package/src/core/control-plane-client.ts +15 -0
  204. package/src/core/utils/initControlPlane.test.ts +187 -0
  205. package/src/core/utils/initControlPlane.ts +194 -20
  206. package/src/security/custom-element-security.test.ts +51 -0
  207. package/src/security/custom-element-security.ts +33 -15
  208. package/src/server/control-plane-types.ts +1 -0
  209. package/src/server/control-plane.test.ts +69 -0
  210. package/src/server/control-plane.ts +45 -3
  211. package/src/server/mcp/tools/built-in-tools.ts +80 -5
  212. package/src/server/mcp/tools/client-tool-relay.ts +183 -26
  213. package/src/server/mcp/tools/mcp-tools.test.ts +140 -0
  214. package/src/server/middleware/static-files.test.ts +142 -0
  215. package/src/server/middleware/static-files.ts +66 -8
  216. package/src/testing/jest-setup.ts +1 -0
  217. package/src/worker/utils/handleMessage.ts +4 -1
  218. package/dist/lib/initControlPlane-CsO4UR6_.js.map +0 -1
  219. package/dist/lib/readGroupFileBytes-Bve-e5e_.js.map +0 -1
  220. package/dist/lib/shadow-claw-file-viewer-BfDHHgpA.js.map +0 -1
  221. package/dist/lib/shadow-claw-pages-D54jF1Qq.js.map +0 -1
  222. package/dist/lib/trusted-types-CkLDpLiZ.js.map +0 -1
  223. package/dist/public/custom-element-security-NCqQ5lmC.js +0 -1
  224. package/dist/public/downloadGroupFile-G0BZQgQg.js +0 -1
  225. package/dist/public/effect-rHYr4nfu.js +0 -1
  226. package/dist/public/file-viewer-8PdCURDD.js +0 -1
  227. package/dist/public/initControlPlane-CcGXax0A.js +0 -1
  228. package/dist/public/peerjs-BZdxWQFv.js +0 -1
  229. package/dist/public/syncWebMcpRegistration-DD42YsYV.js +0 -1
  230. package/dist/public/toast-HAq8XWFj.js +0 -58
@@ -596,8 +596,6 @@ export class ShadowClawA2UI extends ShadowClawElement {
596
596
  return;
597
597
  }
598
598
 
599
- const db = await getDb();
600
-
601
599
  // Collect all deferred media elements
602
600
  const mediaElements = [
603
601
  ...Array.from(root.querySelectorAll("img[data-a2ui-workspace-src]")),
@@ -605,15 +603,22 @@ export class ShadowClawA2UI extends ShadowClawElement {
605
603
  ...Array.from(root.querySelectorAll("audio[data-a2ui-workspace-src]")),
606
604
  ];
607
605
 
606
+ // Also resolve poster URLs for videos
607
+ const videosWithPoster = Array.from(
608
+ root.querySelectorAll("video[data-a2ui-workspace-poster]"),
609
+ );
610
+
611
+ if (mediaElements.length === 0 && videosWithPoster.length === 0) {
612
+ return;
613
+ }
614
+
615
+ const db = await getDb();
616
+
608
617
  // Parallelize all media blob URL conversions using Promise.all
609
618
  await Promise.all(
610
619
  mediaElements.map((el) => this.#resolveSingleMediaToBlobUrl(el, db)),
611
620
  );
612
621
 
613
- // Also resolve poster URLs for videos
614
- const videosWithPoster = Array.from(
615
- root.querySelectorAll("video[data-a2ui-workspace-poster]"),
616
- );
617
622
  await Promise.all(
618
623
  videosWithPoster.map((el) =>
619
624
  this.#resolveVideoPosterToBlobUrl(el as HTMLVideoElement, db),
@@ -667,7 +667,7 @@ describe("shadow-claw-file-viewer", () => {
667
667
  configScript.textContent = JSON.stringify({
668
668
  customElements: {
669
669
  scripts: [
670
- "pages/main/block-garden-adapter.js",
670
+ { src: "pages/main/block-garden-adapter.js", hasInit: true },
671
671
  "https://kherrick.github.io/block-garden/block-garden-bundle-min.mjs",
672
672
  ],
673
673
  },
@@ -686,6 +686,13 @@ describe("shadow-claw-file-viewer", () => {
686
686
  expect(srcdoc).toContain(
687
687
  'src="https://kherrick.github.io/block-garden/block-garden-bundle-min.mjs"',
688
688
  );
689
+ // Has init script for block-garden-adapter.js because hasInit is true
690
+ expect(srcdoc).toContain('import("/block-garden-adapter.js")');
691
+ expect(srcdoc).toContain('m?.init === "function"');
692
+ // Does NOT have init script for block-garden-bundle-min.mjs
693
+ expect(srcdoc).not.toContain(
694
+ 'import("https://kherrick.github.io/block-garden/block-garden-bundle-min.mjs")',
695
+ );
689
696
 
690
697
  document.head.removeChild(configScript);
691
698
  setAllowedCustomElementHostPatterns([]);
@@ -51,7 +51,7 @@ import type { Config } from "dompurify";
51
51
  import type { ShadowClawDatabase } from "../../db/types.js";
52
52
 
53
53
  import {
54
- getApprovedCustomElementScripts,
54
+ getApprovedCustomElementScriptDescriptors,
55
55
  getIframeCsp,
56
56
  getIframeSandboxPolicy,
57
57
  isAllowedCustomElement,
@@ -1386,7 +1386,7 @@ export class ShadowClawFileViewer extends ShadowClawElement {
1386
1386
  "/assets/iframe-storage-bridge.js",
1387
1387
  );
1388
1388
 
1389
- const approvedScripts = getApprovedCustomElementScripts();
1389
+ const approvedScripts = getApprovedCustomElementScriptDescriptors();
1390
1390
  const cspContent = getIframeCsp(nonce);
1391
1391
 
1392
1392
  const configScript =
@@ -1399,11 +1399,11 @@ export class ShadowClawFileViewer extends ShadowClawElement {
1399
1399
  : "";
1400
1400
 
1401
1401
  const searchScriptHtml = searchParams
1402
- ? `<script nonce="${nonce}">(function(){try{Object.defineProperty(window.location,"search",{get:function(){return ${JSON.stringify(searchParams)};},configurable:true});}catch(e){}})();</script>`
1402
+ ? `<script nonce="${nonce}">(function(){try{Object.defineProperty(globalThis.location,"search",{get:function(){return ${JSON.stringify(searchParams)};},configurable:true});}catch(e){}})();</script>`
1403
1403
  : "";
1404
1404
 
1405
1405
  const customElementScriptsHtml = approvedScripts
1406
- .map((src) => {
1406
+ .map(({ src, hasInit }) => {
1407
1407
  const isExternal =
1408
1408
  src.startsWith("http://") ||
1409
1409
  src.startsWith("https://") ||
@@ -1415,7 +1415,15 @@ export class ShadowClawFileViewer extends ShadowClawElement {
1415
1415
  ? src
1416
1416
  : `/${src.replace(/^pages\/main\//, "")}`,
1417
1417
  );
1418
- return `<script type="module" src="${resolvedSrc}" nonce="${nonce}"></script>`;
1418
+ const tags = [
1419
+ `<script type="module" src="${resolvedSrc}" nonce="${nonce}"></script>`,
1420
+ ];
1421
+ if (hasInit) {
1422
+ tags.push(
1423
+ `<script type="module" nonce="${nonce}">import("${resolvedSrc}").then((m) => { try { if (typeof m?.init === "function") m.init(); } catch (e) { console.error("[ShadowClaw] Error executing init for " + ${JSON.stringify(resolvedSrc)}, e); } }).catch(() => {});</script>`,
1424
+ );
1425
+ }
1426
+ return tags.join("\n");
1419
1427
  })
1420
1428
  .join("\n");
1421
1429
 
@@ -10,7 +10,7 @@ import {
10
10
  getIframeThemeStyleHtml,
11
11
  } from "../../../ui/iframe-theme.js";
12
12
  import {
13
- getApprovedCustomElementScripts,
13
+ getApprovedCustomElementScriptDescriptors,
14
14
  getIframeCsp,
15
15
  isAllowedCustomElement,
16
16
  } from "../../../security/custom-element-security.js";
@@ -85,7 +85,7 @@ export async function buildHtmlPageSrcdoc({
85
85
  "/assets/iframe-storage-bridge.js",
86
86
  );
87
87
 
88
- const approvedScripts = getApprovedCustomElementScripts();
88
+ const approvedScripts = getApprovedCustomElementScriptDescriptors();
89
89
  const cspContent = getIframeCsp(nonce);
90
90
 
91
91
  const configScript =
@@ -98,11 +98,11 @@ export async function buildHtmlPageSrcdoc({
98
98
  : "";
99
99
 
100
100
  const searchScriptHtml = searchParams
101
- ? `<script nonce="${nonce}">(function(){try{Object.defineProperty(window.location,"search",{get:function(){return ${JSON.stringify(searchParams)};},configurable:true});}catch(e){}})();</script>`
101
+ ? `<script nonce="${nonce}">(function(){try{Object.defineProperty(globalThis.location,"search",{get:function(){return ${JSON.stringify(searchParams)};},configurable:true});}catch(e){}})();</script>`
102
102
  : "";
103
103
 
104
104
  const customElementScriptsHtml = approvedScripts
105
- .map((src) => {
105
+ .map(({ src, hasInit }) => {
106
106
  const isExternal =
107
107
  src.startsWith("http://") ||
108
108
  src.startsWith("https://") ||
@@ -112,7 +112,15 @@ export async function buildHtmlPageSrcdoc({
112
112
  : applyBasePath(
113
113
  src.startsWith("/") ? src : `/${src.replace(/^pages\/main\//, "")}`,
114
114
  );
115
- return `<script type="module" src="${resolvedSrc}" nonce="${nonce}"></script>`;
115
+ const tags = [
116
+ `<script type="module" src="${resolvedSrc}" nonce="${nonce}"></script>`,
117
+ ];
118
+ if (hasInit) {
119
+ tags.push(
120
+ `<script type="module" nonce="${nonce}">import("${resolvedSrc}").then((m) => { try { if (typeof m?.init === "function") m.init(); } catch (e) { console.error("[ShadowClaw] Error executing init for " + ${JSON.stringify(resolvedSrc)}, e); } }).catch(() => {});</script>`,
121
+ );
122
+ }
123
+ return tags.join("\n");
116
124
  })
117
125
  .join("\n");
118
126
 
@@ -247,6 +247,9 @@ export class ControlPlaneClient {
247
247
 
248
248
  public disconnect(): void {
249
249
  this._isDestroyed = true;
250
+ try {
251
+ this._sendUnregister();
252
+ } catch (_) {}
250
253
  this._cleanupConnection();
251
254
  if (this._reconnectTimer) {
252
255
  clearTimeout(this._reconnectTimer);
@@ -331,6 +334,18 @@ export class ControlPlaneClient {
331
334
  });
332
335
  }
333
336
 
337
+ private _sendUnregister(): void {
338
+ if (!this._clientId) return;
339
+ const payload = {
340
+ clientId: this._clientId,
341
+ };
342
+ this._sendMessage({
343
+ id: ulid(),
344
+ type: "client:unregister",
345
+ payload,
346
+ });
347
+ }
348
+
334
349
  private _sendMessage(msg: ControlMessage): void {
335
350
  if (this._transport === "websocket") {
336
351
  if (
@@ -588,6 +588,193 @@ describe("initControlPlane", () => {
588
588
  Object.defineProperty(navigator, "modelContext", origNavMC);
589
589
  });
590
590
 
591
+ it("handles native Chromium WebMCP invoke-tool passing ModelContextTool to executeTool", async () => {
592
+ const origDocMC = Object.getOwnPropertyDescriptor(
593
+ document,
594
+ "modelContext",
595
+ );
596
+ const origNavMC = Object.getOwnPropertyDescriptor(
597
+ navigator,
598
+ "modelContext",
599
+ );
600
+ delete (navigator as any).modelContext;
601
+
602
+ const nativeModelContextTool = {
603
+ name: "list_files",
604
+ description: "List files and directories",
605
+ inputSchema: '{"type":"object"}',
606
+ title: "list_files",
607
+ origin: "http://localhost:8888",
608
+ window: globalThis.window,
609
+ };
610
+
611
+ const mockExecuteTool = (jest.fn() as any).mockImplementation(
612
+ async (tool: any, _inputArgsJson: string) => {
613
+ // Native Chromium strictly requires the tool object from getTools()
614
+ if (tool !== nativeModelContextTool) {
615
+ throw new TypeError(
616
+ "Failed to execute 'executeTool' on 'ModelContext': parameter 1 is not of type 'ModelContextTool'",
617
+ );
618
+ }
619
+ return `.agents/ MEMORY.md index.html -/`;
620
+ },
621
+ );
622
+
623
+ Object.defineProperty(document, "modelContext", {
624
+ value: {
625
+ getTools: (jest.fn() as any).mockResolvedValue([
626
+ nativeModelContextTool,
627
+ ]),
628
+ executeTool: mockExecuteTool,
629
+ },
630
+ configurable: true,
631
+ });
632
+
633
+ try {
634
+ const res = await executeClientControlCommand(
635
+ "invoke-tool",
636
+ { toolName: "list_files", input: {} },
637
+ {},
638
+ );
639
+
640
+ expect(res).toEqual({
641
+ result: ".agents/ MEMORY.md index.html -/",
642
+ });
643
+ expect(mockExecuteTool).toHaveBeenCalledWith(
644
+ nativeModelContextTool,
645
+ "{}",
646
+ );
647
+ } finally {
648
+ if (origDocMC)
649
+ Object.defineProperty(document, "modelContext", origDocMC);
650
+ if (origNavMC)
651
+ Object.defineProperty(navigator, "modelContext", origNavMC);
652
+ }
653
+ });
654
+
655
+ it("refuses to execute tool when tool is not registered on client or not enabled in active conversation", async () => {
656
+ const origDocMC = Object.getOwnPropertyDescriptor(
657
+ document,
658
+ "modelContext",
659
+ );
660
+ const origNavMC = Object.getOwnPropertyDescriptor(
661
+ navigator,
662
+ "modelContext",
663
+ );
664
+ delete (navigator as any).modelContext;
665
+
666
+ Object.defineProperty(document, "modelContext", {
667
+ value: {
668
+ getTools: (jest.fn() as any).mockResolvedValue([
669
+ {
670
+ name: "list_files",
671
+ description: "List files",
672
+ inputSchema: "{}",
673
+ },
674
+ ]),
675
+ executeTool: jest.fn(),
676
+ },
677
+ configurable: true,
678
+ });
679
+
680
+ try {
681
+ // 1. Client has WebMCP tools registered, but ask_user is not among them
682
+ await expect(
683
+ executeClientControlCommand("invoke-tool", { toolName: "ask_user" }),
684
+ ).rejects.toThrow(
685
+ "Tool 'ask_user' is not enabled or registered on this client.",
686
+ );
687
+
688
+ // 2. Conversation group has restricted toolTags
689
+ const { orchestratorStore } =
690
+ await import("../../stores/orchestrator.js");
691
+ const groupsSpy = jest
692
+ .spyOn(orchestratorStore, "groups", "get")
693
+ .mockReturnValue([
694
+ { groupId: "br:restricted", toolTags: ["read_file"] } as any,
695
+ ]);
696
+
697
+ try {
698
+ await expect(
699
+ executeClientControlCommand(
700
+ "invoke-tool",
701
+ { toolName: "write_file", groupId: "br:restricted" },
702
+ { orchestrator: { activeGroupId: "br:restricted" } as any },
703
+ ),
704
+ ).rejects.toThrow(
705
+ "Tool 'write_file' is not enabled in the active conversation (br:restricted).",
706
+ );
707
+ } finally {
708
+ groupsSpy.mockRestore();
709
+ }
710
+ } finally {
711
+ if (origDocMC)
712
+ Object.defineProperty(document, "modelContext", origDocMC);
713
+ if (origNavMC)
714
+ Object.defineProperty(navigator, "modelContext", origNavMC);
715
+ }
716
+ });
717
+
718
+ it("falls back to worker when modelContext executeTool throws", async () => {
719
+ const origDocMC = Object.getOwnPropertyDescriptor(
720
+ document,
721
+ "modelContext",
722
+ );
723
+ delete (document as any).modelContext;
724
+
725
+ let workerHandler: any;
726
+ const mockWorker: any = {
727
+ addEventListener: jest.fn((event: string, handler: any) => {
728
+ if (event === "message") {
729
+ workerHandler = handler;
730
+ }
731
+ }),
732
+ removeEventListener: jest.fn(),
733
+ postMessage: jest.fn((msg: any) => {
734
+ setTimeout(() => {
735
+ workerHandler({
736
+ data: {
737
+ type: "execute-tool-result",
738
+ callId: msg.callId,
739
+ result: "worker result for " + msg.payload.name,
740
+ },
741
+ });
742
+ }, 0);
743
+ }),
744
+ };
745
+
746
+ // Define modelContext where executeTool throws
747
+ Object.defineProperty(document, "modelContext", {
748
+ value: {
749
+ getTools: (jest.fn() as any).mockResolvedValue([]),
750
+ executeTool: (jest.fn() as any).mockRejectedValue(
751
+ new Error("Native runtime fault"),
752
+ ),
753
+ },
754
+ configurable: true,
755
+ });
756
+
757
+ try {
758
+ const res = await executeClientControlCommand(
759
+ "invoke-tool",
760
+ { toolName: "list_files", input: { path: "docs" } },
761
+ {
762
+ orchestrator: {
763
+ agentWorker: mockWorker,
764
+ activeGroupId: "br:main",
765
+ } as any,
766
+ },
767
+ );
768
+
769
+ expect(res).toEqual({
770
+ result: "worker result for list_files",
771
+ });
772
+ } finally {
773
+ if (origDocMC)
774
+ Object.defineProperty(document, "modelContext", origDocMC);
775
+ }
776
+ });
777
+
591
778
  it("handles send-message when orchestrator is not ready", async () => {
592
779
  const { orchestratorStore } =
593
780
  await import("../../stores/orchestrator.js");
@@ -293,7 +293,44 @@ export async function executeClientControlCommand(
293
293
  }
294
294
  } catch (_) {}
295
295
 
296
- // Fallback: list built-in TOOL_DEFINITIONS
296
+ // Fallback: filter by conversation group toolTags if active
297
+ try {
298
+ const { orchestratorStore } =
299
+ await import("../../stores/orchestrator.js");
300
+ const orch =
301
+ options.orchestrator ||
302
+ orchestratorStore.orchestrator ||
303
+ (typeof document !== "undefined"
304
+ ? (document.querySelector("shadow-claw") as any)?.orchestrator
305
+ : null);
306
+ const activeGroupId =
307
+ args.groupId ||
308
+ orch?.activeGroupId ||
309
+ orchestratorStore.activeGroupId ||
310
+ "br:main";
311
+ const group = orchestratorStore.groups.find(
312
+ (g) => g.groupId === activeGroupId,
313
+ );
314
+
315
+ if (group?.toolTags && group.toolTags.length > 0) {
316
+ const { TOOL_DEFINITIONS } =
317
+ await import("../../subsystems/tools/index.js");
318
+ const { parseWebMcpInputSchema } =
319
+ await import("../../subsystems/mcp/webmcp.js");
320
+ const activeTools = TOOL_DEFINITIONS.filter((t) =>
321
+ group.toolTags!.includes(t.name),
322
+ );
323
+ return {
324
+ tools: activeTools.map((t) => ({
325
+ name: t.name,
326
+ description: t.description,
327
+ inputSchema: parseWebMcpInputSchema(t.input_schema),
328
+ })),
329
+ };
330
+ }
331
+ } catch (_) {}
332
+
333
+ // Default fallback: list built-in TOOL_DEFINITIONS
297
334
  const { TOOL_DEFINITIONS } =
298
335
  await import("../../subsystems/tools/index.js");
299
336
  const { parseWebMcpInputSchema } =
@@ -311,51 +348,185 @@ export async function executeClientControlCommand(
311
348
  if (!args.toolName) {
312
349
  throw new Error("Missing toolName parameter");
313
350
  }
351
+
352
+ // 1. Validate tool against active conversation group and toolsStore
353
+ try {
354
+ const { orchestratorStore } =
355
+ await import("../../stores/orchestrator.js");
356
+ const { toolsStore } = await import("../../stores/tools.js");
357
+ const orch =
358
+ options.orchestrator ||
359
+ orchestratorStore.orchestrator ||
360
+ (typeof document !== "undefined"
361
+ ? (document.querySelector("shadow-claw") as any)?.orchestrator
362
+ : null);
363
+ const activeGroupId =
364
+ args.groupId ||
365
+ orch?.activeGroupId ||
366
+ orchestratorStore.activeGroupId ||
367
+ "br:main";
368
+ const group = orchestratorStore.groups.find(
369
+ (g) => g.groupId === activeGroupId,
370
+ );
371
+
372
+ if (group?.toolTags && group.toolTags.length > 0) {
373
+ if (!group.toolTags.includes(args.toolName)) {
374
+ throw new Error(
375
+ `Tool '${args.toolName}' is not enabled in the active conversation (${activeGroupId}).`,
376
+ );
377
+ }
378
+ } else if (
379
+ toolsStore?.allTools &&
380
+ toolsStore.allTools.some((t: any) => t.name === args.toolName) &&
381
+ toolsStore?.enabledToolNames &&
382
+ toolsStore.enabledToolNames.size > 0
383
+ ) {
384
+ const isBuiltIn = toolsStore.enabledToolNames.has(args.toolName);
385
+ const isDeclarative = toolsStore.isDeclarativeToolEnabled(
386
+ args.toolName,
387
+ );
388
+ if (!isBuiltIn && !isDeclarative) {
389
+ throw new Error(
390
+ `Tool '${args.toolName}' is not enabled on this client.`,
391
+ );
392
+ }
393
+ }
394
+ } catch (err: any) {
395
+ if (err.message && err.message.includes("is not enabled")) {
396
+ throw err;
397
+ }
398
+ }
399
+
314
400
  const ctx =
315
401
  (typeof document !== "undefined" && (document as any).modelContext) ||
316
402
  (typeof navigator !== "undefined" && (navigator as any).modelContext);
317
403
 
318
404
  if (ctx) {
405
+ let matchedTool: any;
319
406
  if (typeof ctx.getTools === "function") {
320
407
  try {
321
408
  const tools = await ctx.getTools();
322
- const tool = Array.isArray(tools)
323
- ? tools.find((t: any) => t.name === args.toolName)
324
- : undefined;
325
- if (tool && typeof tool.execute === "function") {
326
- const res = await tool.execute(args.input || {});
409
+ if (Array.isArray(tools) && tools.length > 0) {
410
+ matchedTool = tools.find((t: any) => t.name === args.toolName);
411
+ if (!matchedTool) {
412
+ throw new Error(
413
+ `Tool '${args.toolName}' is not enabled or registered on this client.`,
414
+ );
415
+ }
416
+ } else {
417
+ matchedTool = Array.isArray(tools)
418
+ ? tools.find((t: any) => t.name === args.toolName)
419
+ : undefined;
420
+ }
421
+ if (matchedTool && typeof matchedTool.execute === "function") {
422
+ const res = await matchedTool.execute(
423
+ typeof args.input === "string"
424
+ ? JSON.parse(args.input)
425
+ : args.input || {},
426
+ );
327
427
  return { result: res };
328
428
  }
329
- } catch (_) {}
429
+ } catch (err: any) {
430
+ if (err.message && err.message.includes("is not enabled")) {
431
+ throw err;
432
+ }
433
+ }
330
434
  }
435
+
436
+ const inputStr =
437
+ typeof args.input === "string"
438
+ ? args.input
439
+ : JSON.stringify(args.input || {});
440
+
331
441
  if (typeof ctx.executeTool === "function") {
332
- const res = await ctx.executeTool(
333
- { name: args.toolName },
334
- typeof args.input === "string"
335
- ? args.input
336
- : JSON.stringify(args.input || {}),
337
- );
338
- return { result: res };
442
+ // 1. Pass the actual ModelContextTool object from getTools() if available (required by native Chromium)
443
+ if (matchedTool) {
444
+ try {
445
+ const res = await ctx.executeTool(matchedTool, inputStr);
446
+ return { result: res };
447
+ } catch (_) {}
448
+ }
449
+
450
+ // 2. Pass { name: toolName } object (accepted by polyfill / mocks)
451
+ try {
452
+ const res = await ctx.executeTool(
453
+ { name: args.toolName },
454
+ inputStr,
455
+ );
456
+ return { result: res };
457
+ } catch (_) {}
458
+
459
+ // 3. Pass toolName string directly (accepted by some preview shims)
460
+ try {
461
+ const res = await ctx.executeTool(args.toolName, inputStr);
462
+ return { result: res };
463
+ } catch (_) {}
339
464
  }
465
+
466
+ // 4. Check navigator.modelContextTesting if present
467
+ if (
468
+ typeof navigator !== "undefined" &&
469
+ (navigator as any).modelContextTesting &&
470
+ typeof (navigator as any).modelContextTesting.executeTool ===
471
+ "function"
472
+ ) {
473
+ try {
474
+ const res = await (
475
+ navigator as any
476
+ ).modelContextTesting.executeTool(args.toolName, inputStr);
477
+ return { result: res };
478
+ } catch (_) {}
479
+ }
480
+
481
+ // 5. Check callTool (MCP-B / extension runtime)
482
+ if (typeof ctx.callTool === "function") {
483
+ try {
484
+ const res = await ctx.callTool({
485
+ name: args.toolName,
486
+ arguments:
487
+ typeof args.input === "string"
488
+ ? JSON.parse(args.input)
489
+ : args.input || {},
490
+ });
491
+ return { result: res };
492
+ } catch (_) {}
493
+ }
494
+
495
+ // 6. Check invoke
340
496
  if (typeof ctx.invoke === "function") {
341
- const res = await ctx.invoke(args.toolName, args.input || {});
342
- return { result: res };
497
+ try {
498
+ const res = await ctx.invoke(
499
+ args.toolName,
500
+ typeof args.input === "string"
501
+ ? JSON.parse(args.input)
502
+ : args.input || {},
503
+ );
504
+ return { result: res };
505
+ } catch (_) {}
343
506
  }
344
507
  }
345
508
 
346
509
  // Worker fallback if agent worker is active
347
510
  const { orchestratorStore } =
348
511
  await import("../../stores/orchestrator.js");
349
- const orch = options.orchestrator || orchestratorStore.orchestrator;
512
+ const orch =
513
+ options.orchestrator ||
514
+ orchestratorStore.orchestrator ||
515
+ (typeof document !== "undefined"
516
+ ? (document.querySelector("shadow-claw") as any)?.orchestrator
517
+ : null);
350
518
  const worker: any = orch?.agentWorker;
351
519
  if (worker && typeof worker.postMessage === "function") {
352
520
  const callId =
353
521
  Date.now().toString(36) + Math.random().toString(36).slice(2);
522
+ const isInteractive = args.toolName === "ask_user";
523
+ const fallbackTimeoutMs =
524
+ args.timeoutMs || (isInteractive ? 300000 : 60000);
354
525
  const result = await new Promise((resolve, reject) => {
355
526
  const timeout = setTimeout(() => {
356
527
  worker.removeEventListener("message", handler);
357
528
  reject(new Error("Timeout waiting for tool execution"));
358
- }, 60000);
529
+ }, fallbackTimeoutMs);
359
530
 
360
531
  const handler = (event: MessageEvent) => {
361
532
  const data = event.data;
@@ -380,8 +551,11 @@ export async function executeClientControlCommand(
380
551
  callId,
381
552
  payload: {
382
553
  name: args.toolName,
383
- input: args.input || {},
384
- groupId: args.groupId || orch.activeGroupId || "br:main",
554
+ input:
555
+ typeof args.input === "string"
556
+ ? JSON.parse(args.input)
557
+ : args.input || {},
558
+ groupId: args.groupId || orch?.activeGroupId || "br:main",
385
559
  },
386
560
  });
387
561
  });