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.
- package/.agents/skills/main/skill-creator/SKILL.md +39 -0
- package/.agents/skills/main/skill-creator/references/declarative-pipelines.md +44 -0
- package/.agents/skills/main/skill-creator/references/declarative-tools.md +35 -0
- package/.agents/skills/main/skill-creator/references/frontmatter-spec.md +50 -0
- package/.well-known/agent-skills/index.json +14 -0
- package/README.md +5 -3
- package/bin/build/build.mjs +146 -15
- package/bin/build/build.test.mjs +57 -0
- package/bin/cli.mjs +26 -0
- package/bin/commands/mcp.mjs +277 -55
- package/bin/commands/mcp.test.mjs +275 -1
- package/bin/commands/skills-index.mjs +328 -0
- package/bin/commands/skills-index.test.mjs +161 -0
- package/bin/site-config/utils/custom-elements/get-approved-custom-element-scripts.test.mjs +2 -0
- package/bin/site-config/utils/custom-elements/sanitize-embedded-custom-element-scripts.test.mjs +10 -1
- package/dist/lib/components/index.js +25 -25
- package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/settings/shadow-claw-git.js +1 -1
- package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
- package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
- package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
- package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw-a2ui.js +1 -1
- package/dist/lib/components/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/shadow-claw-channels.js +1 -1
- package/dist/lib/components/shadow-claw-chat.js +1 -1
- package/dist/lib/components/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/shadow-claw-conversations.js +1 -1
- package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
- package/dist/lib/components/shadow-claw-files.js +1 -1
- package/dist/lib/components/shadow-claw-git.js +1 -1
- package/dist/lib/components/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/shadow-claw-llm.js +1 -1
- package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/shadow-claw-networking.js +1 -1
- package/dist/lib/components/shadow-claw-page-header.js +1 -1
- package/dist/lib/components/shadow-claw-pages.js +1 -1
- package/dist/lib/components/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/shadow-claw-settings.js +1 -1
- package/dist/lib/components/shadow-claw-storage.js +1 -1
- package/dist/lib/components/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/shadow-claw-tasks.js +1 -1
- package/dist/lib/components/shadow-claw-terminal.js +1 -1
- package/dist/lib/components/shadow-claw-tools.js +1 -1
- package/dist/lib/components/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw.js +1 -1
- package/dist/lib/{configurePeerJs-CrlGan0m.js → configurePeerJs-oODzltaC.js} +2 -2
- package/dist/lib/{configurePeerJs-CrlGan0m.js.map → configurePeerJs-oODzltaC.js.map} +1 -1
- package/dist/lib/core/control-plane-client.d.ts +1 -0
- package/dist/lib/{downloadGroupFile-DboUjmp2.js → downloadGroupFile-DmcBQU1n.js} +2 -2
- package/dist/lib/{downloadGroupFile-DboUjmp2.js.map → downloadGroupFile-DmcBQU1n.js.map} +1 -1
- package/dist/lib/{file-viewer-BrK2Wxgh.js → file-viewer-C3YKnq_-.js} +4 -3
- package/dist/lib/{file-viewer-BrK2Wxgh.js.map → file-viewer-C3YKnq_-.js.map} +1 -1
- package/dist/lib/index.js +26 -26
- package/dist/lib/{initControlPlane-CsO4UR6_.js → initControlPlane-C2zURVIS.js} +86 -13
- package/dist/lib/initControlPlane-C2zURVIS.js.map +1 -0
- package/dist/lib/{markdown-eeSTFnMA.js → markdown-C6EHl9zs.js} +3 -3
- package/dist/lib/{markdown-eeSTFnMA.js.map → markdown-C6EHl9zs.js.map} +1 -1
- package/dist/lib/{orchestrator-CJvoC2Uu.js → orchestrator-CACLa_Ro.js} +7 -6
- package/dist/lib/{orchestrator-CJvoC2Uu.js.map → orchestrator-CACLa_Ro.js.map} +1 -1
- package/dist/lib/{orchestrator-DCfomsn9.js → orchestrator-KRjx0eba.js} +6 -534
- package/dist/lib/{orchestrator-DCfomsn9.js.map → orchestrator-KRjx0eba.js.map} +1 -1
- package/dist/lib/parsePath-DwC_YZjw.js +16 -0
- package/dist/lib/parsePath-DwC_YZjw.js.map +1 -0
- package/dist/lib/{peerjs-Dp02bXcG.js → peerjs-DFWOBcXf.js} +4 -4
- package/dist/lib/{peerjs-Dp02bXcG.js.map → peerjs-DFWOBcXf.js.map} +1 -1
- package/dist/lib/{readGroupFileBytes-Bve-e5e_.js → readGroupFileBytes-C8oOe8Iu.js} +3 -16
- package/dist/lib/readGroupFileBytes-C8oOe8Iu.js.map +1 -0
- package/dist/lib/security/custom-element-security.d.ts +8 -0
- package/dist/lib/server/control-plane-types.d.ts +1 -1
- package/dist/lib/{shadow-claw-D8xINM3c.js → shadow-claw-6UHdiy4a.js} +5 -5
- package/dist/lib/{shadow-claw-D8xINM3c.js.map → shadow-claw-6UHdiy4a.js.map} +1 -1
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js → shadow-claw-a2ui-CpZUTzlh.js} +5 -4
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js.map → shadow-claw-a2ui-CpZUTzlh.js.map} +1 -1
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js → shadow-claw-accounts-03oJ__XC.js} +2 -2
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js.map → shadow-claw-accounts-03oJ__XC.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js → shadow-claw-channel-config-DmfwJc6G.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js.map → shadow-claw-channel-config-DmfwJc6G.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js → shadow-claw-channels-1N6Jl-By.js} +3 -3
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js.map → shadow-claw-channels-1N6Jl-By.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-B16MspoP.js → shadow-claw-chat-BMoqQJRK.js} +10 -10
- package/dist/lib/{shadow-claw-chat-B16MspoP.js.map → shadow-claw-chat-BMoqQJRK.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js → shadow-claw-control-plane-UG0o7pCz.js} +6 -6
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js.map → shadow-claw-control-plane-UG0o7pCz.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js → shadow-claw-conversations-BwXep3iu.js} +4 -3
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js.map → shadow-claw-conversations-BwXep3iu.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-BfDHHgpA.js → shadow-claw-file-viewer-DUxzVxDT.js} +14 -10
- package/dist/lib/shadow-claw-file-viewer-DUxzVxDT.js.map +1 -0
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js → shadow-claw-files-DIREEnT2.js} +7 -7
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js.map → shadow-claw-files-DIREEnT2.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js → shadow-claw-git-Bg1SWgsF.js} +4 -4
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js.map → shadow-claw-git-Bg1SWgsF.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js → shadow-claw-integrations-Ds-CLPhj.js} +2 -2
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js.map → shadow-claw-integrations-Ds-CLPhj.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js → shadow-claw-llm-CazBrVpM.js} +3 -3
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js.map → shadow-claw-llm-CazBrVpM.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js → shadow-claw-mcp-remote-CT5Q-54E.js} +2 -2
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js.map → shadow-claw-mcp-remote-CT5Q-54E.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js → shadow-claw-networking-Df85tCbO.js} +2 -2
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js.map → shadow-claw-networking-Df85tCbO.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js → shadow-claw-page-header-Cti2fzf-.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js.map → shadow-claw-page-header-Cti2fzf-.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-D54jF1Qq.js → shadow-claw-pages-BgPyIEZO.js} +15 -11
- package/dist/lib/shadow-claw-pages-BgPyIEZO.js.map +1 -0
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js → shadow-claw-peerjs-xkBXILed.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js.map → shadow-claw-peerjs-xkBXILed.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js → shadow-claw-settings-CN89xRVU.js} +4 -4
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js.map → shadow-claw-settings-CN89xRVU.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js → shadow-claw-storage-VnhcObCe.js} +3 -3
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js.map → shadow-claw-storage-VnhcObCe.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js → shadow-claw-task-server-jgEC2hHB.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js.map → shadow-claw-task-server-jgEC2hHB.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js → shadow-claw-tasks-i_evIufy.js} +6 -6
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js.map → shadow-claw-tasks-i_evIufy.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js → shadow-claw-terminal-CwScROlu.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js.map → shadow-claw-terminal-CwScROlu.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js → shadow-claw-tools-DD3fyXP4.js} +5 -4
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js.map → shadow-claw-tools-DD3fyXP4.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js → shadow-claw-webvm-DbJq4I3-.js} +2 -2
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js.map → shadow-claw-webvm-DbJq4I3-.js.map} +1 -1
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js → syncWebMcpRegistration-DNLnQ-jz.js} +3 -2
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js.map → syncWebMcpRegistration-DNLnQ-jz.js.map} +1 -1
- package/dist/lib/tools-DwwB9RBa.js +546 -0
- package/dist/lib/tools-DwwB9RBa.js.map +1 -0
- package/dist/lib/{trusted-types-CkLDpLiZ.js → trusted-types-Dx0RZlrd.js} +19 -8
- package/dist/lib/trusted-types-Dx0RZlrd.js.map +1 -0
- package/dist/lib/utils/index.js +1 -1
- package/dist/lib/utils/utils.js +1 -1
- package/dist/public/.well-known/agent-skills/index.json +14 -0
- package/dist/public/AGENTS.md +5 -3
- package/dist/public/README.md +5 -3
- package/dist/public/agent.worker.js +1 -1
- package/dist/public/{configurePeerJs-COcwE0E7.js → configurePeerJs-BqgeBjP7.js} +1 -1
- package/dist/public/custom-element-security-FU04Cq05.js +1 -0
- package/dist/public/dist-B9m1QM9v.js +58 -0
- package/dist/public/docs/README.md +31 -31
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/a2ui.md +11 -1
- package/dist/public/docs/subsystems/cli.md +29 -0
- package/dist/public/docs/subsystems/control-plane.md +8 -2
- package/dist/public/docs/subsystems/custom-element-security.md +19 -9
- package/dist/public/docs/subsystems/mcp-server.md +39 -15
- package/dist/public/docs/subsystems/skills.md +30 -0
- package/dist/public/docs/subsystems/webmcp.md +9 -0
- package/dist/public/downloadGroupFile-D8KRJGGL.js +1 -0
- package/dist/public/e2e/README.md +7 -4
- package/dist/public/{e2e-bridge-CHARpbpW.js → e2e-bridge-Ceqr5kGc.js} +1 -1
- package/dist/public/effect-BJCrpFdp.js +1 -0
- package/dist/public/file-viewer-C-6Jc_tb.js +1 -0
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/initControlPlane-BN9cWyO0.js +1 -0
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{markdown-1I3O6Mmd.js → markdown-C3M4OW_A.js} +1 -1
- package/dist/public/{orchestrator-Cl95w2vb.js → orchestrator-CMbCTjBf.js} +7 -7
- package/dist/public/{orchestrator-D-xlXG92.js → orchestrator-DHjdLtqa.js} +663 -663
- package/dist/public/peerjs-DUCKH-m9.js +1 -0
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-CaoKmIIB.js → shadow-claw-EAt2ml5Y.js} +3 -3
- package/dist/public/{shadow-claw-accounts-CJvYC4li.js → shadow-claw-accounts-xUOLBsQZ.js} +1 -1
- package/dist/public/{shadow-claw-channels-3K62sU5f.js → shadow-claw-channels-XsfuWFF8.js} +4 -4
- package/dist/public/{shadow-claw-chat-Cu2keU2I.js → shadow-claw-chat-CbSaOZ4q.js} +7 -7
- package/dist/public/{shadow-claw-control-plane-D1OwlQI_.js → shadow-claw-control-plane-DHvN3Ukg.js} +2 -2
- package/dist/public/{shadow-claw-conversations-DDtg3BEZ.js → shadow-claw-conversations-BWs5VVfs.js} +2 -2
- package/dist/public/{shadow-claw-file-viewer-BR__1z9S.js → shadow-claw-file-viewer-BWsoW5QS.js} +5 -4
- package/dist/public/{shadow-claw-files-BJSyZ-Bl.js → shadow-claw-files-CG7MqWiV.js} +8 -8
- package/dist/public/{shadow-claw-git-MAjhXRLc.js → shadow-claw-git-DBRPZNav.js} +6 -6
- package/dist/public/{shadow-claw-integrations-82UoFuDR.js → shadow-claw-integrations-BBVnO0Fc.js} +8 -8
- package/dist/public/{shadow-claw-llm-R8j0lwPP.js → shadow-claw-llm-DMvLgKXo.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-BzZoz4LX.js → shadow-claw-mcp-remote-Bkx0gsmF.js} +2 -2
- package/dist/public/{shadow-claw-networking-DLEd9cNm.js → shadow-claw-networking-Dd8Rw31D.js} +2 -2
- package/dist/public/{shadow-claw-notifications-1vwR7TbT.js → shadow-claw-notifications-Dbevj-lq.js} +2 -2
- package/dist/public/{shadow-claw-page-header-DMKbkv3s.js → shadow-claw-page-header-BccSHItx.js} +1 -1
- package/dist/public/{shadow-claw-pages-CjyeIV__.js → shadow-claw-pages-3_lQAv0t.js} +4 -3
- package/dist/public/{shadow-claw-settings-BfbT4dBU.js → shadow-claw-settings-t8TIlgrM.js} +5 -5
- package/dist/public/{shadow-claw-storage-C-mWQQML.js → shadow-claw-storage-CfEXoSIE.js} +2 -2
- package/dist/public/{shadow-claw-task-server-D3lHp0Nc.js → shadow-claw-task-server-DxQSsgRZ.js} +2 -2
- package/dist/public/{shadow-claw-tasks-DVVWEm-c.js → shadow-claw-tasks-DNpicQwz.js} +14 -14
- package/dist/public/{shadow-claw-terminal-BwuTRp-O.js → shadow-claw-terminal-DpY6zU_r.js} +1 -1
- package/dist/public/{shadow-claw-toast-ClCwgzFG.js → shadow-claw-toast-BkrQIP-3.js} +1 -1
- package/dist/public/{shadow-claw-tools-zFLk2nbV.js → shadow-claw-tools-WSwWohnk.js} +2 -2
- package/dist/public/{shadow-claw-webvm-qR9X7fg7.js → shadow-claw-webvm-CP6oFjg5.js} +2 -2
- package/dist/public/syncWebMcpRegistration-DOn9gLnD.js +1 -0
- package/dist/public/toast-BXgUfbyh.js +1 -0
- package/dist/public/tools-DvckJZG2.js +1 -0
- package/dist/server.js +188 -188
- package/package.json +3 -1
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.stories.ts +1380 -93
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.ts +11 -6
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +8 -1
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +13 -5
- package/src/components/shadow-claw-pages/utils/buildHtmlPageSrcdoc.ts +13 -5
- package/src/core/control-plane-client.ts +15 -0
- package/src/core/utils/initControlPlane.test.ts +187 -0
- package/src/core/utils/initControlPlane.ts +194 -20
- package/src/security/custom-element-security.test.ts +51 -0
- package/src/security/custom-element-security.ts +33 -15
- package/src/server/control-plane-types.ts +1 -0
- package/src/server/control-plane.test.ts +69 -0
- package/src/server/control-plane.ts +45 -3
- package/src/server/mcp/tools/built-in-tools.ts +80 -5
- package/src/server/mcp/tools/client-tool-relay.ts +183 -26
- package/src/server/mcp/tools/mcp-tools.test.ts +140 -0
- package/src/server/middleware/static-files.test.ts +142 -0
- package/src/server/middleware/static-files.ts +66 -8
- package/src/testing/jest-setup.ts +1 -0
- package/src/worker/utils/handleMessage.ts +4 -1
- package/dist/lib/initControlPlane-CsO4UR6_.js.map +0 -1
- package/dist/lib/readGroupFileBytes-Bve-e5e_.js.map +0 -1
- package/dist/lib/shadow-claw-file-viewer-BfDHHgpA.js.map +0 -1
- package/dist/lib/shadow-claw-pages-D54jF1Qq.js.map +0 -1
- package/dist/lib/trusted-types-CkLDpLiZ.js.map +0 -1
- package/dist/public/custom-element-security-NCqQ5lmC.js +0 -1
- package/dist/public/downloadGroupFile-G0BZQgQg.js +0 -1
- package/dist/public/effect-rHYr4nfu.js +0 -1
- package/dist/public/file-viewer-8PdCURDD.js +0 -1
- package/dist/public/initControlPlane-CcGXax0A.js +0 -1
- package/dist/public/peerjs-BZdxWQFv.js +0 -1
- package/dist/public/syncWebMcpRegistration-DD42YsYV.js +0 -1
- 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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
:
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
342
|
-
|
|
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 =
|
|
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
|
-
},
|
|
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:
|
|
384
|
-
|
|
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
|
});
|