shadow-claw 1.54.0 → 1.55.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 (286) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/build/build.js +1 -1
  5. package/dist/cli/{chunk-DO2t9bUI.js → chunk--IuVyWfT.js} +24 -23
  6. package/dist/cli/{chunk-2ZYd9gYC.js → chunk--JpdI-Ht.js} +1 -1
  7. package/dist/cli/{chunk-m4Ineemy.js → chunk-4O8yrQuN.js} +1 -1
  8. package/dist/cli/{chunk-BiH9IXnQ.js → chunk-7S9Ywlfy.js} +1 -1
  9. package/dist/cli/{chunk-BmMOdYvk.js → chunk-B0SqqDSK.js} +1 -1
  10. package/dist/cli/chunk-BNaWaEm-.js +1 -0
  11. package/dist/cli/chunk-BXCsxO9e.js +1 -0
  12. package/dist/cli/chunk-Bq37qDoZ.js +18 -0
  13. package/dist/cli/{chunk-C1htf-nO.js → chunk-BrEeCKIo.js} +2 -2
  14. package/dist/cli/{chunk-BI1SOTM1.js → chunk-Bu3ylrhT.js} +8 -8
  15. package/dist/cli/{chunk-3smiGQoQ.js → chunk-C1v_s0Kb.js} +1 -1
  16. package/dist/cli/{chunk-DGeysxz3.js → chunk-C44G533y.js} +1 -1
  17. package/dist/cli/{chunk-9KiBalKu.js → chunk-C7hJLmsL.js} +1 -1
  18. package/dist/cli/{chunk-BFv9XSNd.js → chunk-CAaQWd6t.js} +1 -1
  19. package/dist/cli/chunk-CIkgn5Fh.js +1 -0
  20. package/dist/cli/chunk-CWEIN2kc.js +1 -0
  21. package/dist/cli/{chunk-BDeDZ-0L.js → chunk-CYg1lO1a.js} +1 -1
  22. package/dist/cli/{chunk-DXLmEQ3i.js → chunk-Ce09lwXT.js} +1 -1
  23. package/dist/cli/{chunk-XsfHeN4m.js → chunk-CpdqRSO-.js} +1 -1
  24. package/dist/cli/chunk-DB4x5L-B.js +1 -0
  25. package/dist/cli/{chunk-DaNyMqrk.js → chunk-DBvObsBQ.js} +1 -1
  26. package/dist/cli/chunk-DGPpt7Ka.js +1 -0
  27. package/dist/cli/chunk-DGagHluH.js +1 -0
  28. package/dist/cli/chunk-DUIs23D9.js +1 -0
  29. package/dist/cli/{chunk-CyvfQV16.js → chunk-DaurYOqg.js} +1 -1
  30. package/dist/cli/{chunk-DSFrcSyA.js → chunk-DfwRL0Ue.js} +1 -1
  31. package/dist/cli/{chunk-M2tZrzY5.js → chunk-DudYQk0p.js} +1 -1
  32. package/dist/cli/chunk-DvKo50Tp.js +2 -0
  33. package/dist/cli/{chunk-H_Mw5ypp.js → chunk-Dvrqte5Q.js} +2 -2
  34. package/dist/cli/{chunk-BtNO-m9T.js → chunk-DzhSExU0.js} +1 -1
  35. package/dist/cli/{chunk-IOMO88kZ.js → chunk-ENKlt3i9.js} +1 -1
  36. package/dist/cli/{chunk-Bspgzwsm.js → chunk-FWRwAX75.js} +8 -8
  37. package/dist/cli/chunk-Lf-Z-h48.js +32 -0
  38. package/dist/cli/{chunk-BucXnZ4Z.js → chunk-VRfkSLrD.js} +1 -1
  39. package/dist/cli/chunk-uaTxD024.js +1 -0
  40. package/dist/cli/cli.js +1 -1
  41. package/dist/cli/prerender/dsd-shell/prerender-dsd-shell.js +1 -1
  42. package/dist/headless-agent.js +2 -2
  43. package/dist/lib/components/index.js +25 -25
  44. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  45. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  46. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  47. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  48. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  49. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  50. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  51. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  52. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  53. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  54. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  55. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  56. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  57. package/dist/lib/components/shadow-claw/utils/get-route.d.ts +3 -0
  58. package/dist/lib/components/shadow-claw/utils/sync-slotted-sidebar-active-links.d.ts +1 -1
  59. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  60. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  61. package/dist/lib/components/shadow-claw-channels.js +1 -1
  62. package/dist/lib/components/shadow-claw-chat.js +1 -1
  63. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  64. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  65. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  66. package/dist/lib/components/shadow-claw-files.js +1 -1
  67. package/dist/lib/components/shadow-claw-git.js +1 -1
  68. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  69. package/dist/lib/components/shadow-claw-llm.js +1 -1
  70. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  71. package/dist/lib/components/shadow-claw-networking.js +1 -1
  72. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  73. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  74. package/dist/lib/components/shadow-claw-pages.js +1 -1
  75. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  76. package/dist/lib/components/shadow-claw-settings.js +1 -1
  77. package/dist/lib/components/shadow-claw-storage.js +1 -1
  78. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  79. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  80. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  81. package/dist/lib/components/shadow-claw-tools.js +1 -1
  82. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  83. package/dist/lib/components/shadow-claw.js +1 -1
  84. package/dist/lib/{configure-peer-js-D5jDCtLx.js → configure-peer-js-C9554Slv.js} +2 -2
  85. package/dist/lib/{configure-peer-js-D5jDCtLx.js.map → configure-peer-js-C9554Slv.js.map} +1 -1
  86. package/dist/lib/{file-viewer-B4CL4e0i.js → file-viewer-DBc5dxNy.js} +2 -2
  87. package/dist/lib/{file-viewer-B4CL4e0i.js.map → file-viewer-DBc5dxNy.js.map} +1 -1
  88. package/dist/lib/index.js +28 -27
  89. package/dist/lib/{init-control-plane-l2uFnhGV.js → init-control-plane-D-41Fm5q.js} +12 -12
  90. package/dist/lib/{init-control-plane-l2uFnhGV.js.map → init-control-plane-D-41Fm5q.js.map} +1 -1
  91. package/dist/lib/{markdown-0_p_zfxL.js → markdown-KnDWzWJJ.js} +2 -2
  92. package/dist/lib/{markdown-0_p_zfxL.js.map → markdown-KnDWzWJJ.js.map} +1 -1
  93. package/dist/lib/{orchestrator-D6wm8Sec.js → orchestrator-BEZcqgR5.js} +9 -9
  94. package/dist/lib/{orchestrator-D6wm8Sec.js.map → orchestrator-BEZcqgR5.js.map} +1 -1
  95. package/dist/lib/{orchestrator-Dh-hnIpK.js → orchestrator-Cda13e5U.js} +4 -4
  96. package/dist/lib/{orchestrator-Dh-hnIpK.js.map → orchestrator-Cda13e5U.js.map} +1 -1
  97. package/dist/lib/{peerjs-BWZnsD0B.js → peerjs-DYkUTcee.js} +5 -5
  98. package/dist/lib/{peerjs-BWZnsD0B.js.map → peerjs-DYkUTcee.js.map} +1 -1
  99. package/dist/lib/{push-client-MybDfOdi.js → push-client-CQtCFDaD.js} +3 -3
  100. package/dist/lib/{push-client-MybDfOdi.js.map → push-client-CQtCFDaD.js.map} +1 -1
  101. package/dist/lib/security/custom-element-security.d.ts +8 -4
  102. package/dist/lib/{shadow-claw-PdOpgTxH.js → shadow-claw-DYe9u6Tp.js} +142 -33
  103. package/dist/lib/shadow-claw-DYe9u6Tp.js.map +1 -0
  104. package/dist/lib/{shadow-claw-accounts-3bR76EOb.js → shadow-claw-accounts-D2URoHys.js} +2 -2
  105. package/dist/lib/{shadow-claw-accounts-3bR76EOb.js.map → shadow-claw-accounts-D2URoHys.js.map} +1 -1
  106. package/dist/lib/{shadow-claw-channel-config-ChdZ1iB8.js → shadow-claw-channel-config-buXoz2xv.js} +3 -3
  107. package/dist/lib/{shadow-claw-channel-config-ChdZ1iB8.js.map → shadow-claw-channel-config-buXoz2xv.js.map} +1 -1
  108. package/dist/lib/{shadow-claw-channels-CK9sc7h_.js → shadow-claw-channels-5yVJ1gLk.js} +3 -3
  109. package/dist/lib/{shadow-claw-channels-CK9sc7h_.js.map → shadow-claw-channels-5yVJ1gLk.js.map} +1 -1
  110. package/dist/lib/{shadow-claw-chat-B6uuZnJ7.js → shadow-claw-chat-CVc-7ycM.js} +7 -7
  111. package/dist/lib/{shadow-claw-chat-B6uuZnJ7.js.map → shadow-claw-chat-CVc-7ycM.js.map} +1 -1
  112. package/dist/lib/{shadow-claw-control-plane-BpRcl312.js → shadow-claw-control-plane-B5vsF91B.js} +6 -6
  113. package/dist/lib/{shadow-claw-control-plane-BpRcl312.js.map → shadow-claw-control-plane-B5vsF91B.js.map} +1 -1
  114. package/dist/lib/{shadow-claw-conversations-Dc_Xk2mB.js → shadow-claw-conversations-PGK5zqn3.js} +54 -12
  115. package/dist/lib/{shadow-claw-conversations-Dc_Xk2mB.js.map → shadow-claw-conversations-PGK5zqn3.js.map} +1 -1
  116. package/dist/lib/{shadow-claw-file-viewer-C4x1PtB2.js → shadow-claw-file-viewer-DSqn0hgx.js} +6 -6
  117. package/dist/lib/{shadow-claw-file-viewer-C4x1PtB2.js.map → shadow-claw-file-viewer-DSqn0hgx.js.map} +1 -1
  118. package/dist/lib/{shadow-claw-files-DhnHbXKf.js → shadow-claw-files-vOa9ZWbr.js} +6 -6
  119. package/dist/lib/{shadow-claw-files-DhnHbXKf.js.map → shadow-claw-files-vOa9ZWbr.js.map} +1 -1
  120. package/dist/lib/{shadow-claw-git-BeQ1ElUd.js → shadow-claw-git-D54Z85Bj.js} +4 -4
  121. package/dist/lib/{shadow-claw-git-BeQ1ElUd.js.map → shadow-claw-git-D54Z85Bj.js.map} +1 -1
  122. package/dist/lib/{shadow-claw-integrations-Bbdolk3w.js → shadow-claw-integrations-BTubMH9d.js} +2 -2
  123. package/dist/lib/{shadow-claw-integrations-Bbdolk3w.js.map → shadow-claw-integrations-BTubMH9d.js.map} +1 -1
  124. package/dist/lib/{shadow-claw-llm-DPda4L3C.js → shadow-claw-llm-8koGTNYs.js} +3 -3
  125. package/dist/lib/{shadow-claw-llm-DPda4L3C.js.map → shadow-claw-llm-8koGTNYs.js.map} +1 -1
  126. package/dist/lib/{shadow-claw-mcp-remote-Dn9c30WI.js → shadow-claw-mcp-remote-C2jwBsQt.js} +2 -2
  127. package/dist/lib/{shadow-claw-mcp-remote-Dn9c30WI.js.map → shadow-claw-mcp-remote-C2jwBsQt.js.map} +1 -1
  128. package/dist/lib/{shadow-claw-networking-zqNuyn3-.js → shadow-claw-networking-BV-rna16.js} +2 -2
  129. package/dist/lib/{shadow-claw-networking-zqNuyn3-.js.map → shadow-claw-networking-BV-rna16.js.map} +1 -1
  130. package/dist/lib/{shadow-claw-notifications-DFgdo33v.js → shadow-claw-notifications-Dku7VBLk.js} +2 -2
  131. package/dist/lib/{shadow-claw-notifications-DFgdo33v.js.map → shadow-claw-notifications-Dku7VBLk.js.map} +1 -1
  132. package/dist/lib/{shadow-claw-page-header-B7LFGQRf.js → shadow-claw-page-header-VPjYWfsT.js} +2 -2
  133. package/dist/lib/{shadow-claw-page-header-B7LFGQRf.js.map → shadow-claw-page-header-VPjYWfsT.js.map} +1 -1
  134. package/dist/lib/{shadow-claw-pages-DLspFqdK.js → shadow-claw-pages-DXrM6hgZ.js} +29 -19
  135. package/dist/lib/shadow-claw-pages-DXrM6hgZ.js.map +1 -0
  136. package/dist/lib/{shadow-claw-peerjs-D8xWwuWR.js → shadow-claw-peerjs-D3xonez5.js} +3 -3
  137. package/dist/lib/{shadow-claw-peerjs-D8xWwuWR.js.map → shadow-claw-peerjs-D3xonez5.js.map} +1 -1
  138. package/dist/lib/{shadow-claw-settings-NbvzXASD.js → shadow-claw-settings-DMTx6OS9.js} +5 -5
  139. package/dist/lib/{shadow-claw-settings-NbvzXASD.js.map → shadow-claw-settings-DMTx6OS9.js.map} +1 -1
  140. package/dist/lib/{shadow-claw-storage-B_NAoZmd.js → shadow-claw-storage-BJLnukxa.js} +3 -3
  141. package/dist/lib/{shadow-claw-storage-B_NAoZmd.js.map → shadow-claw-storage-BJLnukxa.js.map} +1 -1
  142. package/dist/lib/{shadow-claw-task-server-BaQJXhYJ.js → shadow-claw-task-server-DcMj20hD.js} +2 -2
  143. package/dist/lib/{shadow-claw-task-server-BaQJXhYJ.js.map → shadow-claw-task-server-DcMj20hD.js.map} +1 -1
  144. package/dist/lib/{shadow-claw-tasks-CAZua0-j.js → shadow-claw-tasks-BQBe4lVi.js} +7 -7
  145. package/dist/lib/{shadow-claw-tasks-CAZua0-j.js.map → shadow-claw-tasks-BQBe4lVi.js.map} +1 -1
  146. package/dist/lib/{shadow-claw-terminal-B2YJhkJL.js → shadow-claw-terminal-DPEzTxXv.js} +2 -2
  147. package/dist/lib/{shadow-claw-terminal-B2YJhkJL.js.map → shadow-claw-terminal-DPEzTxXv.js.map} +1 -1
  148. package/dist/lib/{shadow-claw-tools-CkZWxxTm.js → shadow-claw-tools-BCnU8FaK.js} +6 -6
  149. package/dist/lib/{shadow-claw-tools-CkZWxxTm.js.map → shadow-claw-tools-BCnU8FaK.js.map} +1 -1
  150. package/dist/lib/{shadow-claw-webvm-BL9cgrP9.js → shadow-claw-webvm-CnW-hoJ8.js} +2 -2
  151. package/dist/lib/{shadow-claw-webvm-BL9cgrP9.js.map → shadow-claw-webvm-CnW-hoJ8.js.map} +1 -1
  152. package/dist/lib/subsystems/mcp/webmcp.d.ts +15 -2
  153. package/dist/lib/{sync-web-mcp-registration-dW2MVCj4.js → sync-web-mcp-registration-DwpGjVdo.js} +10 -4
  154. package/dist/lib/sync-web-mcp-registration-DwpGjVdo.js.map +1 -0
  155. package/dist/lib/{tools-D7whby3H.js → tools-CP6G0HF7.js} +5 -6
  156. package/dist/lib/tools-CP6G0HF7.js.map +1 -0
  157. package/dist/lib/{trusted-types-d0cdkut_.js → trusted-types-0cu5Dm6M.js} +29 -4
  158. package/dist/lib/{trusted-types-d0cdkut_.js.map → trusted-types-0cu5Dm6M.js.map} +1 -1
  159. package/dist/lib/utils/index.d.ts +1 -0
  160. package/dist/lib/utils/index.js +4 -2
  161. package/dist/lib/utils/index.js.map +1 -1
  162. package/dist/lib/utils/resolve-page-script-dependencies.d.ts +19 -0
  163. package/dist/lib/utils/resolve-page-script-dependencies.js +98 -0
  164. package/dist/lib/utils/resolve-page-script-dependencies.js.map +1 -0
  165. package/dist/lib/utils/utils.js +1 -1
  166. package/dist/lib/webmcp-Bf9rT7uu.js +2001 -0
  167. package/dist/lib/webmcp-Bf9rT7uu.js.map +1 -0
  168. package/dist/public/.well-known/mcp/server-card.json +1 -1
  169. package/dist/public/.well-known/mcp.json +1 -1
  170. package/dist/public/AGENTS.md +3 -3
  171. package/dist/public/README.md +1 -1
  172. package/dist/public/agent.worker.js +2 -2
  173. package/dist/public/components/shadow-claw/shadow-claw.css +1 -1
  174. package/dist/public/components/shadow-claw/shadow-claw.html +1 -1
  175. package/dist/public/components/shadow-claw-conversations/shadow-claw-conversations.css +1 -1
  176. package/dist/public/components/shadow-claw-conversations/shadow-claw-conversations.html +1 -1
  177. package/dist/public/{configure-peer-js-WLRM7Qa7.js → configure-peer-js-OYl3V8sW.js} +1 -1
  178. package/dist/public/{custom-element-security-Cl991z-x.js → custom-element-security-BR4V40j4.js} +1 -1
  179. package/dist/public/docs/example/article/index.html +3 -3
  180. package/dist/public/docs/example/article.html +3 -3
  181. package/dist/public/docs/publishing/index.html +3 -3
  182. package/dist/public/docs/publishing.html +3 -3
  183. package/dist/public/docs/skill-creator/index.html +3 -3
  184. package/dist/public/docs/skill-creator.html +3 -3
  185. package/dist/public/docs/subsystems/custom-element-security.md +19 -5
  186. package/dist/public/docs/subsystems/pages.md +10 -2
  187. package/dist/public/docs/subsystems/tools.md +1 -2
  188. package/dist/public/docs/subsystems/webmcp.md +41 -6
  189. package/dist/public/{download-group-file-fFjoIIp4.js → download-group-file-BNmK8wac.js} +1 -1
  190. package/dist/public/e2e/README.md +2 -2
  191. package/dist/public/{e2e-bridge-CiSfBRT4.js → e2e-bridge-4phD1JUQ.js} +1 -1
  192. package/dist/public/{file-viewer-DFC3kfeO.js → file-viewer-Dbh3K9nL.js} +1 -1
  193. package/dist/public/index.html +3 -3
  194. package/dist/public/index.js +1 -1
  195. package/dist/public/{init-control-plane-BEbcwmmv.js → init-control-plane-DGOY_RNS.js} +1 -1
  196. package/dist/public/llms.txt +1 -1
  197. package/dist/public/main/index.html +3 -3
  198. package/dist/public/main/memory/index.html +3 -3
  199. package/dist/public/main/memory.html +3 -3
  200. package/dist/public/main.html +3 -3
  201. package/dist/public/{markdown-BeglYzw9.js → markdown-BPZYpfGm.js} +1 -1
  202. package/dist/public/{orchestrator-BlvEG3PO.js → orchestrator-OoUK7CF9.js} +1 -1
  203. package/dist/public/{orchestrator-DTNrntd4.js → orchestrator-a9047bGu.js} +1 -1
  204. package/dist/public/{peerjs-CnBdK94R.js → peerjs-Cbn310Tz.js} +1 -1
  205. package/dist/public/{push-client-DOHbdCyj.js → push-client-Dy4LGJy9.js} +1 -1
  206. package/dist/public/service-worker.js +1 -1
  207. package/dist/public/shadow-claw-B-HWC1uN.js +1180 -0
  208. package/dist/public/{shadow-claw-accounts-C6FXVw2K.js → shadow-claw-accounts-Di-anCZ1.js} +1 -1
  209. package/dist/public/{shadow-claw-channels-tr_r60Kq.js → shadow-claw-channels-RVwCE8pE.js} +1 -1
  210. package/dist/public/{shadow-claw-chat-BAAPhAOs.js → shadow-claw-chat-DI1q8UnX.js} +1 -1
  211. package/dist/public/{shadow-claw-control-plane-C_5VIx8E.js → shadow-claw-control-plane-KXEdLOGl.js} +2 -2
  212. package/dist/public/{shadow-claw-conversations-DGYZdDa3.js → shadow-claw-conversations-BCBGtS30.js} +40 -6
  213. package/dist/public/{shadow-claw-file-viewer-5j2-Z3fN.js → shadow-claw-file-viewer-CW3t6j-w.js} +1 -1
  214. package/dist/public/{shadow-claw-files-DKF4Yy0m.js → shadow-claw-files-JQ-o0ImX.js} +1 -1
  215. package/dist/public/{shadow-claw-git-CtUTJXKT.js → shadow-claw-git-D2M8VixW.js} +2 -2
  216. package/dist/public/{shadow-claw-integrations-CuBy9D3z.js → shadow-claw-integrations-Cgw6v9_1.js} +1 -1
  217. package/dist/public/{shadow-claw-llm-BIfNNjGL.js → shadow-claw-llm-74ieXp7i.js} +1 -1
  218. package/dist/public/{shadow-claw-mcp-remote-C17FXvrb.js → shadow-claw-mcp-remote-BX0BkSmy.js} +1 -1
  219. package/dist/public/{shadow-claw-networking-CjxQ1CLI.js → shadow-claw-networking-DNX8vaIQ.js} +1 -1
  220. package/dist/public/{shadow-claw-notifications-Brn2O3Ps.js → shadow-claw-notifications-BLTaGAny.js} +1 -1
  221. package/dist/public/{shadow-claw-page-header-Bn1P7Cxb.js → shadow-claw-page-header-CjFOdo9v.js} +1 -1
  222. package/dist/public/shadow-claw-pages-DOQqMm0_.js +645 -0
  223. package/dist/public/{shadow-claw-settings-CfDm0hVA.js → shadow-claw-settings-DFtl0Fy8.js} +2 -2
  224. package/dist/public/{shadow-claw-storage-DOOesRzw.js → shadow-claw-storage-gQGExZRq.js} +1 -1
  225. package/dist/public/{shadow-claw-task-server-H36iDDp2.js → shadow-claw-task-server-Ca45uY7S.js} +1 -1
  226. package/dist/public/{shadow-claw-tasks-4JsjSGkN.js → shadow-claw-tasks-CCTaaSdi.js} +1 -1
  227. package/dist/public/{shadow-claw-terminal-CTRTcYz-.js → shadow-claw-terminal-BfgulLkl.js} +1 -1
  228. package/dist/public/{shadow-claw-tools-Dm1LxNf4.js → shadow-claw-tools-V8NA4n18.js} +1 -1
  229. package/dist/public/{shadow-claw-webvm-JzuPe9Nr.js → shadow-claw-webvm-CxxI2bnC.js} +1 -1
  230. package/dist/public/sync-web-mcp-registration-CkCA8UFm.js +1 -0
  231. package/dist/public/{tools-FiTtOlIt.js → tools-CwQq5tJH.js} +2 -2
  232. package/dist/public/webmcp-CmtWOAFm.js +6 -0
  233. package/dist/server.js +2 -2
  234. package/package.json +3 -3
  235. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +16 -0
  236. package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +62 -1
  237. package/src/cli/prerender/pretty-paths/prerender-pretty-paths.test.ts +277 -0
  238. package/src/cli/prerender/pretty-paths/prerender-pretty-paths.ts +28 -1
  239. package/src/cli/site-config/custom-elements/build-custom-element-script-tags.ts +13 -1
  240. package/src/components/shadow-claw/shadow-claw.css +12 -0
  241. package/src/components/shadow-claw/shadow-claw.html +12 -2
  242. package/src/components/shadow-claw/utils/bind-event-listeners.test.ts +18 -0
  243. package/src/components/shadow-claw/utils/bind-event-listeners.ts +14 -0
  244. package/src/components/shadow-claw/utils/get-route.test.ts +35 -0
  245. package/src/components/shadow-claw/utils/get-route.ts +18 -0
  246. package/src/components/shadow-claw/utils/sync-slotted-sidebar-active-links.test.ts +152 -1
  247. package/src/components/shadow-claw/utils/sync-slotted-sidebar-active-links.ts +139 -8
  248. package/src/components/shadow-claw-conversations/shadow-claw-conversations.css +11 -0
  249. package/src/components/shadow-claw-conversations/shadow-claw-conversations.html +27 -4
  250. package/src/components/shadow-claw-conversations/shadow-claw-conversations.ts +32 -4
  251. package/src/components/shadow-claw-pages/shadow-claw-pages-scripts.test.ts +266 -0
  252. package/src/components/shadow-claw-pages/shadow-claw-pages.ts +41 -17
  253. package/src/core/orchestrator/utils/sync-web-mcp-registration.test.ts +18 -1
  254. package/src/core/orchestrator/utils/sync-web-mcp-registration.ts +8 -0
  255. package/src/core/utils/init-control-plane.test.ts +9 -4
  256. package/src/security/custom-element-security.test.ts +65 -0
  257. package/src/security/custom-element-security.ts +67 -4
  258. package/src/stores/tools.test.ts +5 -3
  259. package/src/stores/tools.ts +5 -10
  260. package/src/subsystems/mcp/webmcp.test.ts +84 -13
  261. package/src/subsystems/mcp/webmcp.ts +73 -10
  262. package/src/utils/index.ts +1 -0
  263. package/src/utils/resolve-page-script-dependencies.test.ts +245 -0
  264. package/src/utils/resolve-page-script-dependencies.ts +198 -0
  265. package/dist/cli/chunk-B1hufJeO.js +0 -1
  266. package/dist/cli/chunk-BZpn8npn.js +0 -1
  267. package/dist/cli/chunk-Bw5aOTFr.js +0 -1
  268. package/dist/cli/chunk-C8nH01Gw.js +0 -32
  269. package/dist/cli/chunk-CK1ZoyoE.js +0 -17
  270. package/dist/cli/chunk-CVkhDr-7.js +0 -1
  271. package/dist/cli/chunk-Cum07Vlh.js +0 -1
  272. package/dist/cli/chunk-D3UhDDmV.js +0 -2
  273. package/dist/cli/chunk-D8GI6qj6.js +0 -1
  274. package/dist/cli/chunk-DEH-FvmP.js +0 -1
  275. package/dist/cli/chunk-DksvJ_kf.js +0 -1
  276. package/dist/cli/chunk-RGb9wM4R.js +0 -1
  277. package/dist/lib/shadow-claw-PdOpgTxH.js.map +0 -1
  278. package/dist/lib/shadow-claw-pages-DLspFqdK.js.map +0 -1
  279. package/dist/lib/sync-web-mcp-registration-dW2MVCj4.js.map +0 -1
  280. package/dist/lib/tools-D7whby3H.js.map +0 -1
  281. package/dist/lib/webmcp-CYTNZfkn.js +0 -1687
  282. package/dist/lib/webmcp-CYTNZfkn.js.map +0 -1
  283. package/dist/public/shadow-claw-CnNX-WCh.js +0 -1158
  284. package/dist/public/shadow-claw-pages-BwOOqRWW.js +0 -645
  285. package/dist/public/sync-web-mcp-registration-BnHVUjat.js +0 -1
  286. package/dist/public/webmcp-_TBqXlVW.js +0 -1
@@ -0,0 +1,2001 @@
1
+ import { n as __exportAll } from "./rolldown-runtime-DwyhSnWe.js";
2
+ import { o as DEFAULT_GROUP_ID } from "./config-Dalol4GN.js";
3
+ import { t as TOOL_DEFINITIONS } from "./tools-tRG7Gu0x.js";
4
+ import "./tools-Bj8UJ_6L.js";
5
+
6
+ //#region node_modules/@mcp-b/webmcp-polyfill/dist/index.js
7
+ const annotationNames = [
8
+ "consequentialHint",
9
+ "debugging",
10
+ "readOnlyHint",
11
+ "untrustedContentHint"
12
+ ];
13
+ const protocol = "webmcp-polyfill";
14
+ const deadline = 500;
15
+ const NativeDOMException$1 = globalThis.DOMException;
16
+ var FrameBridge = class {
17
+ #document;
18
+ #window;
19
+ #handlers;
20
+ #knownPeers = /* @__PURE__ */ new Set();
21
+ #unanswered = /* @__PURE__ */ new Set();
22
+ #started = Promise.withResolvers();
23
+ #handshakes = /* @__PURE__ */ new Map();
24
+ #sessions = /* @__PURE__ */ new Set();
25
+ #monitor;
26
+ #hidden = false;
27
+ constructor(view, handlers) {
28
+ this.#window = view;
29
+ this.#document = view.document;
30
+ this.#handlers = handlers;
31
+ this.#window.addEventListener("message", (event) => this.#receive(event), true);
32
+ this.#window.addEventListener("pagehide", () => {
33
+ this.#hidden = true;
34
+ for (const session of this.#sessions) session.end();
35
+ });
36
+ this.#window.addEventListener("pageshow", (event) => {
37
+ if (event.persisted) {
38
+ this.#hidden = false;
39
+ this.#announce();
40
+ }
41
+ });
42
+ for (const frame of this.#announce()) this.#unanswered.add(frame);
43
+ if (this.#unanswered.size === 0) this.#started.resolve();
44
+ else setTimeout(() => {
45
+ this.#unanswered.clear();
46
+ this.#started.resolve();
47
+ }, deadline);
48
+ }
49
+ async allowed() {
50
+ if (this.#hidden) return false;
51
+ return this.#documentAllowed(this.#document);
52
+ }
53
+ async getTools(fromOrigins) {
54
+ await this.#started.promise;
55
+ const ownOrigin = this.#window.origin;
56
+ const origins = [ownOrigin, ...fromOrigins];
57
+ return (await Promise.all(this.#participants().map(async (peer) => {
58
+ if (peer === this.#window) return this.#handlers.getTools(ownOrigin).map((metadata) => toRegisteredTool(metadata, this.#window, ownOrigin));
59
+ try {
60
+ const reply = await this.#request(peer, origins, { kind: "getTools" });
61
+ if (!Array.isArray(reply.value)) throw new TypeError("Expected a tool list from the frame");
62
+ return reply.value.map((value) => toRegisteredTool(readToolMetadata(value), peer, reply.origin));
63
+ } catch (error) {
64
+ if (!(error instanceof NativeDOMException$1 && error.name === "UnknownError")) console.warn("WebMCP: could not read tools from a frame.", error);
65
+ return [];
66
+ }
67
+ }))).flat();
68
+ }
69
+ async execute(target, expectedOrigin, name, serializedInput, signal) {
70
+ const reply = await this.#request(target, [expectedOrigin], {
71
+ kind: "execute",
72
+ name,
73
+ input: serializedInput
74
+ }, signal);
75
+ if (typeof reply.value !== "string") throw executionError();
76
+ return reply.value;
77
+ }
78
+ async notify(exposedTo) {
79
+ await this.#started.promise;
80
+ const origins = [this.#window.origin, ...exposedTo];
81
+ for (const peer of this.#participants()) {
82
+ if (peer === this.#window) {
83
+ await this.#handlers.changed();
84
+ continue;
85
+ }
86
+ try {
87
+ await this.#request(peer, origins, { kind: "changed" });
88
+ } catch {}
89
+ }
90
+ }
91
+ #request(target, origins, request, signal) {
92
+ return new Promise((resolve, reject) => {
93
+ signal?.throwIfAborted();
94
+ if (this.#hidden || !this.#related(target)) {
95
+ reject(executionError());
96
+ return;
97
+ }
98
+ const id = crypto.randomUUID();
99
+ const { port1, port2 } = new MessageChannel();
100
+ let peerOrigin;
101
+ const close = () => {
102
+ clearTimeout(timer);
103
+ stopWatching();
104
+ signal?.removeEventListener("abort", abort);
105
+ this.#handshakes.delete(id);
106
+ port1.close();
107
+ };
108
+ const fail = (error = executionError()) => {
109
+ port1.postMessage({ kind: "cancel" });
110
+ close();
111
+ reject(error);
112
+ };
113
+ const abort = () => fail(signal?.reason);
114
+ const expire = () => {
115
+ if (peerOrigin === void 0) this.#forget(target);
116
+ fail();
117
+ };
118
+ const connected = (event) => {
119
+ if (event.source !== target) return;
120
+ this.#handshakes.delete(id);
121
+ if (origins && !origins.includes(event.origin)) {
122
+ fail();
123
+ return;
124
+ }
125
+ peerOrigin = event.origin;
126
+ clearTimeout(timer);
127
+ if (request.kind !== "execute" && request.kind !== "changed") timer = setTimeout(expire, deadline);
128
+ port1.postMessage(request);
129
+ };
130
+ let timer = setTimeout(expire, deadline);
131
+ const stopWatching = this.#watch(target, fail);
132
+ port1.onmessage = (event) => {
133
+ const reply = record(event.data);
134
+ if (reply?.kind === "error") {
135
+ fail();
136
+ return;
137
+ }
138
+ if (reply?.kind !== "result" || peerOrigin === void 0) return;
139
+ close();
140
+ resolve({
141
+ value: reply.value,
142
+ origin: peerOrigin
143
+ });
144
+ };
145
+ port1.onmessageerror = () => fail();
146
+ signal?.addEventListener("abort", abort, { once: true });
147
+ this.#handshakes.set(id, connected);
148
+ try {
149
+ target.postMessage(`${protocol}:connect:${id}`, "*", [port2]);
150
+ } catch {
151
+ port2.close();
152
+ fail();
153
+ }
154
+ });
155
+ }
156
+ #receive(event) {
157
+ const message = readWindowMessage(event.data);
158
+ if (!message) return;
159
+ event.stopImmediatePropagation();
160
+ const source = event.source;
161
+ if (!event.isTrusted || !source || !isWindow(source) || !this.#related(source) || event.origin === "null" || this.#hidden) return;
162
+ if (message.kind === "announce") {
163
+ const isNew = !this.#knownPeers.has(source);
164
+ this.#learn(source);
165
+ source.postMessage(`${protocol}:present`, event.origin);
166
+ if (!isNew || this.#window !== this.#window.top) return;
167
+ for (const peer of this.#knownPeers) {
168
+ if (peer === source) continue;
169
+ peer.postMessage(`${protocol}:refresh`, "*");
170
+ }
171
+ return;
172
+ }
173
+ if (message.kind === "present") {
174
+ this.#learn(source);
175
+ return;
176
+ }
177
+ if (message.kind === "refresh") {
178
+ if (source === this.#window.top) this.#announce();
179
+ return;
180
+ }
181
+ if (message.kind === "ready") {
182
+ this.#handshakes.get(message.id)?.(event);
183
+ return;
184
+ }
185
+ const [port] = event.ports;
186
+ if (message.kind !== "connect" || !message.id || !port || event.ports.length !== 1) return;
187
+ this.#learn(source);
188
+ this.#accept(source, event.origin, message.id, port);
189
+ }
190
+ #accept(source, origin, id, port) {
191
+ const controller = new AbortController();
192
+ let requested = false;
193
+ const close = () => {
194
+ clearTimeout(timer);
195
+ stopWatching();
196
+ port.close();
197
+ };
198
+ const cancel = () => {
199
+ controller.abort();
200
+ close();
201
+ };
202
+ const fail = () => {
203
+ port.postMessage({ kind: "error" });
204
+ cancel();
205
+ };
206
+ const timer = setTimeout(fail, deadline);
207
+ const stopWatching = this.#watch(source, fail);
208
+ port.onmessageerror = fail;
209
+ port.onmessage = async (event) => {
210
+ const request = record(event.data);
211
+ if (request?.kind === "cancel") {
212
+ cancel();
213
+ return;
214
+ }
215
+ if (requested) return;
216
+ requested = true;
217
+ clearTimeout(timer);
218
+ try {
219
+ const value = await this.#respond(source, origin, request, controller.signal);
220
+ port.postMessage({
221
+ kind: "result",
222
+ value
223
+ });
224
+ close();
225
+ } catch {
226
+ fail();
227
+ }
228
+ };
229
+ source.postMessage(`${protocol}:ready:${id}`, origin);
230
+ }
231
+ async #respond(source, origin, request, signal) {
232
+ if (!request || !this.#related(source)) throw executionError();
233
+ if (request.kind === "permission") {
234
+ const { childIndex, childOrigin } = request;
235
+ if (typeof childIndex !== "number" || !Number.isInteger(childIndex) || childIndex < 0 || typeof childOrigin !== "string") throw executionError();
236
+ return this.#childPermission(childIndex, childOrigin);
237
+ }
238
+ if (!await this.allowed() || !await this.#sourceAllowed(source, origin)) throw executionError();
239
+ if (!this.#related(source)) throw executionError();
240
+ signal.throwIfAborted();
241
+ if (request.kind === "getTools") return this.#handlers.getTools(origin);
242
+ if (request.kind === "execute" && typeof request.name === "string" && typeof request.input === "string") return this.#handlers.execute(origin, request.name, request.input, signal);
243
+ if (request.kind === "changed") {
244
+ await this.#handlers.changed();
245
+ return;
246
+ }
247
+ throw executionError();
248
+ }
249
+ async #childPermission(childIndex, childOrigin) {
250
+ const child = this.#window[childIndex];
251
+ return child !== void 0 && await this.allowed() && childAllowed(this.#document, child, childOrigin);
252
+ }
253
+ async #documentAllowed(owner) {
254
+ const view = activeWindow(owner);
255
+ if (!view) return false;
256
+ const policy = readToolsPolicy(owner);
257
+ if (policy !== void 0) return policy;
258
+ if (view === view.parent) return true;
259
+ const parentOrigin = view.location.ancestorOrigins?.[0];
260
+ const parentDocument = readDocument(view.parent, parentOrigin === void 0 || parentOrigin === view.origin);
261
+ if (!parentDocument) return this.#parentPermission(view, view.origin);
262
+ return childAllowed(parentDocument, view, view.origin) && await this.#documentAllowed(parentDocument);
263
+ }
264
+ async #sourceAllowed(source, origin) {
265
+ const sourceDocument = readDocument(source, origin === this.#window.origin);
266
+ return sourceDocument ? this.#documentAllowed(sourceDocument) : this.#parentPermission(source, origin);
267
+ }
268
+ async #parentPermission(child, origin) {
269
+ const parent = child.parent;
270
+ if (parent === child) return true;
271
+ let childIndex = -1;
272
+ for (let index = 0; index < parent.length; index++) if (parent[index] === child) {
273
+ childIndex = index;
274
+ break;
275
+ }
276
+ if (childIndex < 0) return false;
277
+ if (parent === this.#window) return await this.allowed() && childAllowed(this.#document, child, origin);
278
+ await this.#started.promise;
279
+ if (!this.#knownPeers.has(parent)) return false;
280
+ try {
281
+ const reply = await this.#request(parent, void 0, {
282
+ kind: "permission",
283
+ childIndex,
284
+ childOrigin: origin
285
+ });
286
+ return reply.value === true && await this.#sourceAllowed(parent, reply.origin);
287
+ } catch {
288
+ return false;
289
+ }
290
+ }
291
+ #related(peer) {
292
+ try {
293
+ return !peer.closed && peer !== this.#window && peer.top === this.#window.top;
294
+ } catch {
295
+ return false;
296
+ }
297
+ }
298
+ #tree() {
299
+ if (this.#window.closed) return [];
300
+ for (const peer of this.#knownPeers) if (!this.#related(peer)) this.#knownPeers.delete(peer);
301
+ const candidates = [this.#window, ...this.#knownPeers];
302
+ for (let ancestor = this.#window.parent; ancestor !== ancestor.parent; ancestor = ancestor.parent) candidates.push(ancestor);
303
+ const tree = /* @__PURE__ */ new Set();
304
+ const pending = [this.#window.top];
305
+ while (pending.length) {
306
+ const frame = pending.pop();
307
+ if (tree.has(frame)) continue;
308
+ tree.add(frame);
309
+ const children = [];
310
+ for (let index = 0; index < frame.length; index++) {
311
+ const child = frame[index];
312
+ if (child) children.push(child);
313
+ }
314
+ for (const candidate of candidates) if (candidate !== frame && candidate.parent === frame && !children.includes(candidate)) children.push(candidate);
315
+ pending.push(...children.reverse());
316
+ }
317
+ return Array.from(tree).filter((frame) => frame === this.#window || this.#related(frame));
318
+ }
319
+ #participants() {
320
+ return this.#tree().filter((frame) => frame === this.#window || this.#knownPeers.has(frame));
321
+ }
322
+ #announce() {
323
+ const others = this.#tree().filter((frame) => frame !== this.#window);
324
+ for (const frame of others) frame.postMessage(`${protocol}:announce`, "*");
325
+ return others;
326
+ }
327
+ #learn(peer) {
328
+ this.#knownPeers.add(peer);
329
+ this.#unanswered.delete(peer);
330
+ if (this.#unanswered.size === 0) this.#started.resolve();
331
+ }
332
+ #forget(peer) {
333
+ this.#knownPeers.delete(peer);
334
+ if (this.#related(peer)) peer.postMessage(`${protocol}:announce`, "*");
335
+ }
336
+ #watch(peer, end) {
337
+ const session = {
338
+ peer,
339
+ end
340
+ };
341
+ this.#sessions.add(session);
342
+ if (this.#monitor === void 0) this.#monitor = this.#window.setInterval(() => {
343
+ for (const open of this.#sessions) if (!this.#related(open.peer)) open.end();
344
+ }, 100);
345
+ return () => {
346
+ this.#sessions.delete(session);
347
+ if (this.#sessions.size === 0) {
348
+ this.#window.clearInterval(this.#monitor);
349
+ this.#monitor = void 0;
350
+ }
351
+ };
352
+ }
353
+ };
354
+ function childAllowed(owner, child, origin) {
355
+ const ownerOrigin = owner.defaultView?.origin;
356
+ const frame = findFrame(owner, child, origin === ownerOrigin);
357
+ if (!frame) return false;
358
+ const directive = (frame.getAttribute("allow") ?? "").split(";").map((part) => part.trim().split(/\s+/u)).find(([name]) => name === "tools");
359
+ if (!directive) return origin === ownerOrigin;
360
+ const sources = directive.length === 1 ? ["'src'"] : directive.slice(1);
361
+ if (sources.includes("'none'")) return false;
362
+ const src = frame.hasAttribute("srcdoc") ? null : frame.getAttribute("src");
363
+ const srcURL = src ? URL.parse(src, owner.baseURI) : null;
364
+ const sourceOrigin = !src || srcURL?.protocol === "about:" ? ownerOrigin : srcURL?.origin;
365
+ return sources.some((source) => source === "*" || source === "'self'" && origin === ownerOrigin || source === "'src'" && origin === sourceOrigin || source === origin);
366
+ }
367
+ function readDocument(view, sameOrigin) {
368
+ if (!sameOrigin) return;
369
+ try {
370
+ return view.document;
371
+ } catch {
372
+ return;
373
+ }
374
+ }
375
+ function findFrame(owner, child, sameOrigin) {
376
+ if (sameOrigin) try {
377
+ const frame = child.frameElement;
378
+ if (frame?.ownerDocument === owner) return frame;
379
+ } catch {}
380
+ return searchFrames(owner, child);
381
+ }
382
+ function searchFrames(root, child) {
383
+ const frames = root.querySelectorAll("iframe, frame");
384
+ for (const frame of frames) if (frame.contentWindow === child) return frame;
385
+ for (const element of root.querySelectorAll("*")) {
386
+ if (!element.shadowRoot) continue;
387
+ const frame = searchFrames(element.shadowRoot, child);
388
+ if (frame) return frame;
389
+ }
390
+ }
391
+ function activeWindow(owner) {
392
+ const view = owner.defaultView;
393
+ return view && !view.closed && view.document === owner ? view : void 0;
394
+ }
395
+ function readToolsPolicy(owner) {
396
+ const source = owner;
397
+ const policy = record(source.permissionsPolicy ?? source.featurePolicy);
398
+ if (typeof policy?.features !== "function" || typeof policy.allowsFeature !== "function") return;
399
+ const features = policy.features();
400
+ return Array.isArray(features) && features.includes("tools") ? Boolean(policy.allowsFeature("tools")) : void 0;
401
+ }
402
+ function toRegisteredTool(metadata, owner, origin) {
403
+ const inputSchema = metadata.serializedSchema === void 0 ? void 0 : JSON.parse(metadata.serializedSchema);
404
+ return {
405
+ ...metadata.annotations !== void 0 && { annotations: { ...metadata.annotations } },
406
+ description: metadata.description,
407
+ ...inputSchema !== void 0 && { inputSchema },
408
+ name: metadata.name,
409
+ origin,
410
+ title: metadata.title,
411
+ window: owner
412
+ };
413
+ }
414
+ function readToolMetadata(value) {
415
+ const tool = record(value);
416
+ if (!tool || typeof tool.name !== "string" || typeof tool.title !== "string" || typeof tool.description !== "string" || tool.serializedSchema !== void 0 && typeof tool.serializedSchema !== "string") throw new TypeError("Invalid tool metadata from the frame");
417
+ const metadata = {
418
+ name: tool.name,
419
+ title: tool.title,
420
+ description: tool.description,
421
+ annotations: void 0,
422
+ serializedSchema: tool.serializedSchema
423
+ };
424
+ if (tool.annotations === void 0) return metadata;
425
+ const hints = record(tool.annotations);
426
+ if (!hints) throw new TypeError("Invalid tool annotations from the frame");
427
+ const annotations = {};
428
+ for (const name of annotationNames) {
429
+ const hint = hints[name];
430
+ if (hint === void 0) continue;
431
+ if (typeof hint !== "boolean") throw new TypeError(`Invalid ${name} annotation from the frame`);
432
+ annotations[name] = hint;
433
+ }
434
+ metadata.annotations = annotations;
435
+ return metadata;
436
+ }
437
+ function readWindowMessage(value) {
438
+ if (typeof value !== "string") return;
439
+ const [namespace, kind = "", id = "", extra] = value.split(":");
440
+ return namespace === protocol && extra === void 0 ? {
441
+ kind,
442
+ id
443
+ } : void 0;
444
+ }
445
+ function record(value) {
446
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return;
447
+ return value;
448
+ }
449
+ function isWindow(value) {
450
+ try {
451
+ return "postMessage" in value && "window" in value;
452
+ } catch {
453
+ return false;
454
+ }
455
+ }
456
+ function executionError() {
457
+ return new NativeDOMException$1("Tool execution failed", "UnknownError");
458
+ }
459
+ const agentInvokedEvents = /* @__PURE__ */ new WeakSet();
460
+ const agentResponses = /* @__PURE__ */ new WeakMap();
461
+ const activeSubmissions = /* @__PURE__ */ new WeakMap();
462
+ function isAgentInvokedSubmitEvent(event) {
463
+ return event.isTrusted && (agentInvokedEvents.has(event) || event.eventPhase !== Event.NONE && event.target instanceof HTMLFormElement && activeSubmissions.has(event.target));
464
+ }
465
+ function respondWithAgentSubmitEvent(event, agentResponse) {
466
+ if (!isAgentInvokedSubmitEvent(event)) throw new DOMException("respondWith() is only available during an agent-invoked submit event", "InvalidStateError");
467
+ if (!event.defaultPrevented) throw new DOMException("respondWith() requires preventDefault() during an agent-invoked submit event", "InvalidStateError");
468
+ if (event.eventPhase === Event.NONE) throw new DOMException("respondWith() is only available while the submit event is being dispatched", "InvalidStateError");
469
+ agentInvokedEvents.add(event);
470
+ const response = Promise.resolve(agentResponse);
471
+ agentResponses.set(event, response);
472
+ if (event.target instanceof HTMLFormElement) activeSubmissions.get(event.target)?.respond(event);
473
+ }
474
+ const TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
475
+ "email",
476
+ "password",
477
+ "search",
478
+ "tel",
479
+ "text",
480
+ "url"
481
+ ]);
482
+ const READONLY_INPUT_TYPES = /* @__PURE__ */ new Set([
483
+ ...TEXT_INPUT_TYPES,
484
+ "date",
485
+ "datetime-local",
486
+ "month",
487
+ "number",
488
+ "time",
489
+ "week"
490
+ ]);
491
+ function isControl(element) {
492
+ return element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement;
493
+ }
494
+ function getFormControls(form) {
495
+ return Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, "elements")?.get?.call(form);
496
+ }
497
+ function getFormAttribute(form, name) {
498
+ return Element.prototype.getAttribute.call(form, name);
499
+ }
500
+ function formHasAttribute(form, name) {
501
+ return Element.prototype.hasAttribute.call(form, name);
502
+ }
503
+ function isConnected(node) {
504
+ return Object.getOwnPropertyDescriptor(Node.prototype, "isConnected")?.get?.call(node);
505
+ }
506
+ function getOpenShadowRoot(element) {
507
+ return Object.getOwnPropertyDescriptor(Element.prototype, "shadowRoot")?.get?.call(element);
508
+ }
509
+ function checkFormValidity(form) {
510
+ return HTMLFormElement.prototype.checkValidity.call(form);
511
+ }
512
+ function requestFormSubmit(form, submitter) {
513
+ HTMLFormElement.prototype.requestSubmit.call(form, submitter);
514
+ }
515
+ function getControls(form) {
516
+ return [...getFormControls(form)].filter((element) => isControl(element) && !element.matches(":disabled") && !("readOnly" in element && element.readOnly && (element instanceof HTMLTextAreaElement || READONLY_INPUT_TYPES.has(element.type))));
517
+ }
518
+ function controlGroups(form) {
519
+ const groups = /* @__PURE__ */ new Map();
520
+ for (const control of getControls(form)) {
521
+ const name = control.name.trim();
522
+ const controls = groups.get(name);
523
+ if (controls) controls.push(control);
524
+ else groups.set(name, [control]);
525
+ }
526
+ return groups;
527
+ }
528
+ function labelText(control) {
529
+ return [...control.labels ?? []].map((label) => {
530
+ const copy = label.cloneNode(true);
531
+ if (!(copy instanceof HTMLElement)) return "";
532
+ copy.querySelectorAll("button, input, meter, output, progress, select, textarea").forEach((element) => element.remove());
533
+ return copy.textContent?.trim() ?? "";
534
+ }).filter(Boolean).join("; ");
535
+ }
536
+ function commonFieldset(form, controls) {
537
+ for (let element = controls[0]?.parentElement; element && element !== form; element = element.parentElement) if (element instanceof HTMLFieldSetElement && controls.every((control) => element.contains(control))) return element;
538
+ }
539
+ function parameterDescription(form, controls) {
540
+ if (controls.length === 1) {
541
+ const control = controls[0];
542
+ if (!control) return void 0;
543
+ return control.getAttribute("toolparamdescription") || labelText(control) || control.getAttribute("aria-description") || void 0;
544
+ }
545
+ return commonFieldset(form, controls)?.getAttribute("toolparamdescription") || void 0;
546
+ }
547
+ function withDescription(schema, form, controls, extra) {
548
+ const description = parameterDescription(form, controls);
549
+ const combined = description && extra ? `${description} (${extra})` : description || extra;
550
+ return combined ? {
551
+ ...schema,
552
+ description: combined
553
+ } : schema;
554
+ }
555
+ function validNumberAttribute(input, name) {
556
+ const raw = input.getAttribute(name);
557
+ if (raw === null || raw.trim() === "") return void 0;
558
+ const value = Number(raw);
559
+ return Number.isFinite(value) ? value : void 0;
560
+ }
561
+ function validStep(input, fallback) {
562
+ const step = Number(input.getAttribute("step"));
563
+ return Number.isFinite(step) && step > 0 ? step : fallback;
564
+ }
565
+ function isStepBaseMultiple(stepBase, step) {
566
+ const quotient = stepBase / step;
567
+ return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * 16;
568
+ }
569
+ function withPattern(schema, input) {
570
+ const pattern = input.getAttribute("pattern");
571
+ if (pattern === null) return schema;
572
+ try {
573
+ new RegExp(pattern, "v");
574
+ } catch {
575
+ return schema;
576
+ }
577
+ return {
578
+ ...schema,
579
+ pattern
580
+ };
581
+ }
582
+ function numberSchema(input) {
583
+ const schema = { type: "number" };
584
+ const minimum = validNumberAttribute(input, "min");
585
+ const maximum = validNumberAttribute(input, "max");
586
+ if (minimum !== void 0) schema.minimum = minimum;
587
+ if (maximum !== void 0) schema.maximum = maximum;
588
+ if (input.getAttribute("step") !== "any") {
589
+ const step = validStep(input, 1);
590
+ const rawValue = Number(input.getAttribute("value"));
591
+ if (isStepBaseMultiple(minimum ?? (Number.isFinite(rawValue) ? rawValue : 0), step)) schema.multipleOf = step;
592
+ }
593
+ return schema;
594
+ }
595
+ function temporalFormat(input, datePrefix) {
596
+ const step = validStep(input, 60);
597
+ if (step < 1) return `${datePrefix}(:[0-5][0-9](\\.[0-9]{1,3})?)?$`;
598
+ if (step < 60) return `${datePrefix}(:[0-5][0-9])?$`;
599
+ return `${datePrefix}$`;
600
+ }
601
+ function optionSchemas(options) {
602
+ return {
603
+ anyOf: options.map((option) => ({
604
+ type: "string",
605
+ const: option.value,
606
+ title: option.textContent ?? ""
607
+ })),
608
+ enum: options.map((option) => option.value)
609
+ };
610
+ }
611
+ function groupChoiceSchemas(controls) {
612
+ return {
613
+ anyOf: controls.map((control) => {
614
+ const title = labelText(control);
615
+ const schema = {
616
+ type: "string",
617
+ const: control.value
618
+ };
619
+ if (title) schema.title = title;
620
+ return schema;
621
+ }),
622
+ enum: controls.map((control) => control.value)
623
+ };
624
+ }
625
+ function isInput(control, type) {
626
+ return control instanceof HTMLInputElement && control.type === type;
627
+ }
628
+ function inputSchema(input) {
629
+ if (TEXT_INPUT_TYPES.has(input.type)) return withPattern({ type: "string" }, input);
630
+ switch (input.type) {
631
+ case "hidden": return input.getAttribute("toolparamdescription") ? { type: "string" } : void 0;
632
+ case "number": return withPattern(numberSchema(input), input);
633
+ case "range": {
634
+ const schema = numberSchema(input);
635
+ schema.minimum ??= 0;
636
+ schema.maximum ??= 100;
637
+ return schema;
638
+ }
639
+ case "checkbox": return { type: "boolean" };
640
+ case "date": return {
641
+ type: "string",
642
+ format: "date"
643
+ };
644
+ case "month": return {
645
+ type: "string",
646
+ format: "^[0-9]{4}-(0[1-9]|1[0-2])$"
647
+ };
648
+ case "week": return {
649
+ type: "string",
650
+ format: "^[0-9]{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$"
651
+ };
652
+ case "time": return {
653
+ type: "string",
654
+ format: temporalFormat(input, "^([01][0-9]|2[0-3]):[0-5][0-9]")
655
+ };
656
+ case "datetime-local": return {
657
+ type: "string",
658
+ format: temporalFormat(input, "^[0-9]{4}-(0[1-9]|1[0-2])-[0-9]{2}T([01][0-9]|2[0-3]):[0-5][0-9]")
659
+ };
660
+ case "color": return {
661
+ type: "string",
662
+ format: "^#[0-9a-zA-Z]{6}$"
663
+ };
664
+ default: return;
665
+ }
666
+ }
667
+ function parameterSchema(form, controls) {
668
+ const [first] = controls;
669
+ if (!first) return void 0;
670
+ let schema;
671
+ if (controls.every((control) => isInput(control, "radio"))) schema = {
672
+ type: "string",
673
+ ...groupChoiceSchemas(controls)
674
+ };
675
+ else if (controls.length > 1) {
676
+ if (controls.every((control) => isInput(control, "checkbox"))) schema = {
677
+ type: "array",
678
+ items: {
679
+ type: "string",
680
+ ...groupChoiceSchemas(controls)
681
+ },
682
+ uniqueItems: true
683
+ };
684
+ } else if (first instanceof HTMLTextAreaElement) schema = { type: "string" };
685
+ else if (first instanceof HTMLSelectElement) {
686
+ const choices = optionSchemas([...first.options]);
687
+ schema = first.multiple ? {
688
+ type: "array",
689
+ items: {
690
+ type: "string",
691
+ ...choices
692
+ },
693
+ uniqueItems: true
694
+ } : {
695
+ type: "string",
696
+ ...choices
697
+ };
698
+ } else schema = inputSchema(first);
699
+ const dateHint = first.type === "date" ? "Dates MUST be provided in 'YYYY-MM-DD' format." : void 0;
700
+ return schema && withDescription(schema, form, controls, dateHint);
701
+ }
702
+ function synthesizeSchema(form) {
703
+ const properties = {};
704
+ const required = [];
705
+ for (const [name, controls] of controlGroups(form)) {
706
+ if (!name) continue;
707
+ const schema = parameterSchema(form, controls);
708
+ if (!schema) continue;
709
+ Object.defineProperty(properties, name, {
710
+ configurable: true,
711
+ enumerable: true,
712
+ value: schema,
713
+ writable: true
714
+ });
715
+ if (controls.some((control) => control.required)) required.push(name);
716
+ }
717
+ return {
718
+ type: "object",
719
+ properties,
720
+ required
721
+ };
722
+ }
723
+ function toolDefinition(form) {
724
+ return {
725
+ name: getFormAttribute(form, "toolname") ?? "",
726
+ title: getFormAttribute(form, "tooltitle") ?? "",
727
+ description: getFormAttribute(form, "tooldescription") ?? "",
728
+ inputSchema: synthesizeSchema(form),
729
+ autosubmit: formHasAttribute(form, "toolautosubmit")
730
+ };
731
+ }
732
+ function toFormString(value) {
733
+ if (typeof value === "string" || typeof value === "boolean") return String(value);
734
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
735
+ }
736
+ function toFormBoolean(value) {
737
+ if (typeof value === "boolean") return value;
738
+ if (typeof value === "number" && Number.isInteger(value)) return value !== 0;
739
+ if (typeof value !== "string") return void 0;
740
+ if (value === "1" || value.toLowerCase() === "true") return true;
741
+ if (value === "0" || value.toLowerCase() === "false") return false;
742
+ }
743
+ function hasUniqueAllowedValues(value, allowed) {
744
+ if (!Array.isArray(value)) return false;
745
+ const remaining = new Set(allowed);
746
+ for (const item of value) {
747
+ const string = toFormString(item);
748
+ if (string === void 0 || !remaining.delete(string)) return false;
749
+ }
750
+ return true;
751
+ }
752
+ function inputAcceptsValue(input, value) {
753
+ if (value === "") return input.type !== "number" && input.type !== "range";
754
+ const probe = input.ownerDocument.createElement("input");
755
+ probe.type = input.type;
756
+ probe.value = value;
757
+ return probe.value !== "";
758
+ }
759
+ function isFormParameterValue(form, controls, value) {
760
+ const [first] = controls;
761
+ if (!first || !parameterSchema(form, controls)) return false;
762
+ const string = toFormString(value);
763
+ if (controls.every((control) => isInput(control, "radio"))) return controls.some((control) => control.value === string);
764
+ if (controls.length > 1) return hasUniqueAllowedValues(value, new Set(controls.map((control) => control.value)));
765
+ if (first instanceof HTMLSelectElement) {
766
+ const allowed = new Set([...first.options].map((option) => option.value));
767
+ if (first.multiple) return hasUniqueAllowedValues(value, allowed);
768
+ return string !== void 0 && allowed.has(string);
769
+ }
770
+ if (first instanceof HTMLTextAreaElement) return string !== void 0;
771
+ if (first.type === "checkbox") return toFormBoolean(value) !== void 0;
772
+ return string !== void 0 && inputAcceptsValue(first, string);
773
+ }
774
+ function dispatchInputAndChange(control) {
775
+ control.dispatchEvent(new Event("input", {
776
+ bubbles: true,
777
+ composed: true
778
+ }));
779
+ control.dispatchEvent(new Event("change", { bubbles: true }));
780
+ }
781
+ function setValue(control, value) {
782
+ if (control.value === value) return;
783
+ const prototype = control instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
784
+ Object.getOwnPropertyDescriptor(prototype, "value")?.set?.call(control, value);
785
+ dispatchInputAndChange(control);
786
+ }
787
+ function setChecked(control, checked) {
788
+ if (control.checked === checked) return;
789
+ Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "checked")?.set?.call(control, checked);
790
+ dispatchInputAndChange(control);
791
+ }
792
+ function fillParameter(controls, value) {
793
+ const [first] = controls;
794
+ const string = toFormString(value);
795
+ const values = new Set(Array.isArray(value) ? value.map(toFormString) : []);
796
+ if (controls.every((control) => isInput(control, "radio"))) {
797
+ const control = controls.find((candidate) => candidate.value === string);
798
+ if (control) setChecked(control, true);
799
+ } else if (controls.length > 1) {
800
+ for (const control of controls) if (control instanceof HTMLInputElement) setChecked(control, values.has(control.value));
801
+ } else if (first instanceof HTMLSelectElement) {
802
+ if (first.multiple) {
803
+ let changed = false;
804
+ for (const option of first.options) {
805
+ const next = values.has(option.value);
806
+ if (option.selected === next) continue;
807
+ option.selected = next;
808
+ changed = true;
809
+ }
810
+ if (changed) dispatchInputAndChange(first);
811
+ } else if (string !== void 0 && first.value !== string) {
812
+ first.value = string;
813
+ dispatchInputAndChange(first);
814
+ }
815
+ } else if (first && isInput(first, "checkbox")) {
816
+ const checked = toFormBoolean(value);
817
+ if (checked !== void 0) setChecked(first, checked);
818
+ } else if (first && string !== void 0) setValue(first, string);
819
+ }
820
+ function fillForm(form, input) {
821
+ if (Array.isArray(input)) throw executionError();
822
+ const groups = controlGroups(form);
823
+ const parameters = [];
824
+ for (const [name, value] of Object.entries(input)) {
825
+ const controls = groups.get(name);
826
+ if (!controls || !isFormParameterValue(form, controls, value)) throw executionError();
827
+ parameters.push({
828
+ controls,
829
+ value
830
+ });
831
+ }
832
+ for (const { controls, value } of parameters) fillParameter(controls, value);
833
+ }
834
+ function findSubmitter(form) {
835
+ return [...getFormControls(form)].find((element) => !element.matches(":disabled") && (element instanceof HTMLButtonElement && element.type === "submit" || element instanceof HTMLInputElement && ["image", "submit"].includes(element.type)));
836
+ }
837
+ function lifecycleEvent(type, toolName) {
838
+ const event = new Event(type);
839
+ Object.defineProperty(event, "toolName", {
840
+ enumerable: true,
841
+ value: toolName
842
+ });
843
+ return event;
844
+ }
845
+ function waitForSubmission(context, registration, definition, submitter, signal) {
846
+ const { form } = registration;
847
+ registration.cancelPending?.(executionError());
848
+ return new Promise((resolve, reject) => {
849
+ let settled = false;
850
+ const cleanup = () => {
851
+ EventTarget.prototype.removeEventListener.call(form, "invalid", onInvalid, true);
852
+ signal.removeEventListener("abort", onAbort);
853
+ activeSubmissions.delete(form);
854
+ if (registration.cancelPending === cancel) delete registration.cancelPending;
855
+ };
856
+ const finish = (callback) => {
857
+ if (settled) return;
858
+ settled = true;
859
+ cleanup();
860
+ callback();
861
+ };
862
+ const cancel = (reason) => finish(() => reject(reason));
863
+ const settleResponse = (response) => {
864
+ if (registration.cancelPending === cancel) delete registration.cancelPending;
865
+ response.then((value) => finish(() => resolve(value ?? null)), (cause) => finish(() => reject(cause)));
866
+ };
867
+ const onInvalid = (event) => {
868
+ if (!event.isTrusted) return;
869
+ queueMicrotask(() => {
870
+ if (!checkFormValidity(form)) cancel(executionError());
871
+ });
872
+ };
873
+ const onAbort = () => {
874
+ finish(() => {
875
+ context.dispatchEvent(lifecycleEvent("toolcancel", definition.name));
876
+ reject(signal.reason);
877
+ });
878
+ };
879
+ activeSubmissions.set(form, {
880
+ complete(event) {
881
+ setTimeout(() => {
882
+ const response = agentResponses.get(event);
883
+ if (response) settleResponse(response);
884
+ else if (event.defaultPrevented) cancel(executionError());
885
+ else finish(() => resolve(null));
886
+ });
887
+ },
888
+ direct() {
889
+ finish(() => resolve(null));
890
+ },
891
+ respond(event) {
892
+ queueMicrotask(() => {
893
+ const response = agentResponses.get(event);
894
+ if (response) settleResponse(response);
895
+ });
896
+ }
897
+ });
898
+ registration.cancelPending = cancel;
899
+ EventTarget.prototype.addEventListener.call(form, "invalid", onInvalid, true);
900
+ signal.addEventListener("abort", onAbort, { once: true });
901
+ if (!definition.autosubmit) {
902
+ submitter?.focus();
903
+ context.dispatchEvent(lifecycleEvent("toolactivated", definition.name));
904
+ return;
905
+ }
906
+ try {
907
+ requestFormSubmit(form, submitter);
908
+ context.dispatchEvent(lifecycleEvent("toolactivated", definition.name));
909
+ } catch (error) {
910
+ cancel(error);
911
+ }
912
+ });
913
+ }
914
+ /** Adds declarative tools until the vendored upstream implements them. */
915
+ function installWebMCPDeclarativeExtensions(context) {
916
+ const prototype = SubmitEvent.prototype;
917
+ if ("agentInvoked" in prototype && "respondWith" in prototype) return;
918
+ if (!("agentInvoked" in prototype)) Object.defineProperty(prototype, "agentInvoked", {
919
+ configurable: true,
920
+ enumerable: true,
921
+ get() {
922
+ return isAgentInvokedSubmitEvent(this);
923
+ }
924
+ });
925
+ if (!("respondWith" in prototype)) Object.defineProperty(prototype, "respondWith", {
926
+ configurable: true,
927
+ enumerable: true,
928
+ writable: true,
929
+ value(agentResponse) {
930
+ respondWithAgentSubmitEvent(this, agentResponse);
931
+ }
932
+ });
933
+ const registrations = /* @__PURE__ */ new Map();
934
+ const blockedDefinitions = /* @__PURE__ */ new Map();
935
+ const observers = /* @__PURE__ */ new Map();
936
+ const onSubmit = (event) => {
937
+ if (!(event instanceof SubmitEvent) || !event.isTrusted || !(event.target instanceof HTMLFormElement)) return;
938
+ const submission = activeSubmissions.get(event.target);
939
+ if (!submission) return;
940
+ agentInvokedEvents.add(event);
941
+ submission.complete(event);
942
+ };
943
+ const onReset = (event) => {
944
+ if (!event.isTrusted || !(event.target instanceof HTMLFormElement)) return;
945
+ const form = event.target;
946
+ setTimeout(() => {
947
+ if (event.defaultPrevented) return;
948
+ registrations.get(form)?.cancelPending?.(executionError());
949
+ });
950
+ };
951
+ function stopObservingRoot(root) {
952
+ observers.get(root)?.disconnect();
953
+ observers.delete(root);
954
+ root.removeEventListener("reset", onReset, true);
955
+ root.removeEventListener("submit", onSubmit, true);
956
+ }
957
+ function observeRoot(root) {
958
+ if (observers.has(root)) return;
959
+ const observer = new MutationObserver(sync);
960
+ observers.set(root, observer);
961
+ observer.observe(root, {
962
+ attributes: true,
963
+ characterData: true,
964
+ childList: true,
965
+ subtree: true
966
+ });
967
+ root.addEventListener("reset", onReset, true);
968
+ root.addEventListener("submit", onSubmit, true);
969
+ }
970
+ function sync() {
971
+ observeRoot(document);
972
+ for (const root of observers.keys()) if (root instanceof ShadowRoot && !root.host.isConnected) stopObservingRoot(root);
973
+ const candidates = /* @__PURE__ */ new Set();
974
+ const selected = /* @__PURE__ */ new Map();
975
+ const selectedByName = /* @__PURE__ */ new Map();
976
+ for (const root of observers.keys()) for (const element of root.querySelectorAll("*")) {
977
+ const shadowRoot = getOpenShadowRoot(element);
978
+ if (shadowRoot) observeRoot(shadowRoot);
979
+ if (!(element instanceof HTMLFormElement) || !formHasAttribute(element, "toolname") || !formHasAttribute(element, "tooldescription") || !isConnected(element)) continue;
980
+ const form = element;
981
+ candidates.add(form);
982
+ const definition = toolDefinition(form);
983
+ const fingerprint = JSON.stringify(definition);
984
+ if (blockedDefinitions.get(form) === fingerprint) continue;
985
+ blockedDefinitions.delete(form);
986
+ const existingForm = selectedByName.get(definition.name);
987
+ if (existingForm) {
988
+ const holdsName = (candidate) => registrations.get(candidate)?.name === definition.name;
989
+ if (!holdsName(form) || holdsName(existingForm)) {
990
+ blockedDefinitions.set(form, fingerprint);
991
+ continue;
992
+ }
993
+ const existingSelection = selected.get(existingForm);
994
+ if (existingSelection) blockedDefinitions.set(existingForm, existingSelection.fingerprint);
995
+ selected.delete(existingForm);
996
+ }
997
+ selectedByName.set(definition.name, form);
998
+ selected.set(form, {
999
+ definition,
1000
+ fingerprint
1001
+ });
1002
+ }
1003
+ for (const form of blockedDefinitions.keys()) if (!candidates.has(form)) blockedDefinitions.delete(form);
1004
+ for (const [form, registration] of registrations) if (selected.get(form)?.fingerprint !== registration.fingerprint) {
1005
+ registration.cancelPending?.(executionError());
1006
+ registration.controller.abort();
1007
+ registrations.delete(form);
1008
+ }
1009
+ for (const [form, { definition, fingerprint }] of selected) {
1010
+ if (registrations.has(form)) continue;
1011
+ const controller = new AbortController();
1012
+ const registration = {
1013
+ controller,
1014
+ fingerprint,
1015
+ form,
1016
+ name: definition.name
1017
+ };
1018
+ registrations.set(form, registration);
1019
+ context.registerTool({
1020
+ name: definition.name,
1021
+ title: definition.title,
1022
+ description: definition.description,
1023
+ inputSchema: definition.inputSchema,
1024
+ execute(input, options) {
1025
+ const submitter = findSubmitter(form);
1026
+ if (!definition.autosubmit && !submitter) throw executionError();
1027
+ fillForm(form, input);
1028
+ return waitForSubmission(context, registration, definition, submitter, options.signal);
1029
+ }
1030
+ }, { signal: controller.signal }).catch((cause) => {
1031
+ controller.abort();
1032
+ if (cause instanceof DOMException && cause.name === "AbortError") return;
1033
+ console.error(`[webmcp] declarative form tool "${definition.name}" was not registered:`, cause);
1034
+ });
1035
+ }
1036
+ }
1037
+ const attachShadowDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, "attachShadow");
1038
+ if (attachShadowDescriptor?.configurable) {
1039
+ const nativeAttachShadow = Element.prototype.attachShadow;
1040
+ const attachShadow = function(init) {
1041
+ const root = nativeAttachShadow.call(this, init);
1042
+ if (root.mode === "open" && Object.getOwnPropertyDescriptor(Node.prototype, "ownerDocument")?.get?.call(this) === document && isConnected(this)) observeRoot(root);
1043
+ return root;
1044
+ };
1045
+ Object.defineProperty(Element.prototype, "attachShadow", {
1046
+ ...attachShadowDescriptor,
1047
+ value: attachShadow
1048
+ });
1049
+ }
1050
+ const submitDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, "submit");
1051
+ if (submitDescriptor?.configurable) {
1052
+ const nativeSubmit = HTMLFormElement.prototype.submit;
1053
+ const submit = function() {
1054
+ nativeSubmit.call(this);
1055
+ activeSubmissions.get(this)?.direct();
1056
+ };
1057
+ Object.defineProperty(HTMLFormElement.prototype, "submit", {
1058
+ ...submitDescriptor,
1059
+ value: submit
1060
+ });
1061
+ }
1062
+ sync();
1063
+ }
1064
+ /*!
1065
+ * Copyright (c) 2026 WebMCP polyfill contributors
1066
+ * SPDX-License-Identifier: MIT
1067
+ */
1068
+ const NativeDOMException = globalThis.DOMException;
1069
+ const getWindow = Object.getOwnPropertyDescriptor(globalThis, "window")?.get;
1070
+ const contexts = /* @__PURE__ */ new WeakMap();
1071
+ /**
1072
+ * Install WebMCP in the current window.
1073
+ *
1074
+ * Does nothing outside a secure browser context or when `document.modelContext`
1075
+ * already exists, including partial native implementations. Call before registering
1076
+ * tools in each frame; repeated calls preserve existing contexts and registrations.
1077
+ *
1078
+ * Installing joins cross-frame discovery: the window listens for the polyfill's messages and
1079
+ * announces itself to the other frames of its tree.
1080
+ *
1081
+ * @throws {TypeError} If the window or Document prototype prevents installation.
1082
+ * @example
1083
+ * import { installWebMCP } from "webmcp-polyfill";
1084
+ * installWebMCP();
1085
+ *
1086
+ * @see https://webmachinelearning.github.io/webmcp/#document-extension
1087
+ * @see https://github.com/webmachinelearning/webmcp-polyfill/blob/main/TESTING.md
1088
+ */
1089
+ function installWebMCP$1() {
1090
+ if (typeof document === "undefined" || !globalThis.isSecureContext) return;
1091
+ if ("modelContext" in document) return;
1092
+ const documentPrototype = Document.prototype;
1093
+ const getDefaultView = Object.getOwnPropertyDescriptor(documentPrototype, "defaultView").get;
1094
+ const constructorDescriptor = Object.getOwnPropertyDescriptor(globalThis, "ModelContext");
1095
+ if (!Object.isExtensible(documentPrototype) || constructorDescriptor && !constructorDescriptor.configurable || !constructorDescriptor && !Object.isExtensible(globalThis)) throw new TypeError("Cannot install WebMCP on this realm");
1096
+ Object.defineProperty(globalThis, "ModelContext", {
1097
+ value: modelContextConstructor,
1098
+ configurable: true,
1099
+ writable: true
1100
+ });
1101
+ const { getModelContext } = { getModelContext() {
1102
+ getDefaultView.call(this);
1103
+ let context = contexts.get(this);
1104
+ if (!context) {
1105
+ context = new ModelContextPolyfill(this);
1106
+ contexts.set(this, context);
1107
+ }
1108
+ return context;
1109
+ } };
1110
+ Object.defineProperty(getModelContext, "name", { value: "get modelContext" });
1111
+ Object.defineProperty(documentPrototype, "modelContext", {
1112
+ configurable: true,
1113
+ enumerable: true,
1114
+ get: getModelContext
1115
+ });
1116
+ document.modelContext;
1117
+ }
1118
+ var ModelContextPolyfill = class extends EventTarget {
1119
+ #document;
1120
+ #tools = /* @__PURE__ */ new Map();
1121
+ #frames;
1122
+ #toolchangeHandler = null;
1123
+ #toolchangeListener = (event) => {
1124
+ const handler = this.#toolchangeHandler;
1125
+ if (typeof handler !== "function") return;
1126
+ if (Reflect.apply(handler, this, [event]) === false) Event.prototype.preventDefault.call(event);
1127
+ };
1128
+ constructor(owner) {
1129
+ super();
1130
+ this.#document = owner;
1131
+ const view = activeWindow(owner);
1132
+ if (!view) return;
1133
+ this.#frames = new FrameBridge(view, {
1134
+ getTools: (callerOrigin) => this.#exposedTools(callerOrigin),
1135
+ execute: (callerOrigin, name, serializedInput, signal) => {
1136
+ const ownerOrigin = requireActiveWindow(owner).origin;
1137
+ return this.#executeLocal(name, serializedInput, ownerOrigin, callerOrigin, signal);
1138
+ },
1139
+ changed: () => this.#queueToolChange()
1140
+ });
1141
+ }
1142
+ get ontoolchange() {
1143
+ return this.#toolchangeHandler;
1144
+ }
1145
+ set ontoolchange(handler) {
1146
+ const nextHandler = isObject(handler) ? handler : null;
1147
+ if (!this.#toolchangeHandler && nextHandler) this.addEventListener("toolchange", this.#toolchangeListener);
1148
+ if (this.#toolchangeHandler && !nextHandler) this.removeEventListener("toolchange", this.#toolchangeListener);
1149
+ this.#toolchangeHandler = nextHandler;
1150
+ }
1151
+ async registerTool(tool, options = {}) {
1152
+ const ownerDocument = this.#document;
1153
+ const { name, title, description, annotations, inputSchema, execute } = readToolDefinition(tool);
1154
+ const settings = readDictionary(options);
1155
+ const exposedTo = readOriginSequence(settings.exposedTo);
1156
+ const registrationSignal = readAbortSignal(settings.signal);
1157
+ requireActiveWindow(ownerDocument);
1158
+ const requireUnusedName = () => {
1159
+ if (this.#tools.has(name)) throw new NativeDOMException(`A tool named ${name} is already registered`, "InvalidStateError");
1160
+ };
1161
+ requireUnusedName();
1162
+ if (!/^[A-Za-z0-9_.-]{1,128}$/u.test(name)) throw new NativeDOMException(`Tool names are 1 to 128 characters of ASCII alphanumerics, "_", "-" or ".": ${name}`, "InvalidStateError");
1163
+ if (!description) throw new NativeDOMException("A tool description cannot be empty", "InvalidStateError");
1164
+ const serializedSchema = inputSchema === void 0 ? void 0 : serializeJSON(inputSchema);
1165
+ registrationSignal?.throwIfAborted();
1166
+ const exposedOrigins = parseOrigins(exposedTo);
1167
+ const storedTool = {
1168
+ metadata: {
1169
+ name,
1170
+ title,
1171
+ description,
1172
+ annotations,
1173
+ serializedSchema
1174
+ },
1175
+ execute,
1176
+ exposedTo: exposedOrigins
1177
+ };
1178
+ const frames = await this.#requireFrames();
1179
+ registrationSignal?.throwIfAborted();
1180
+ requireUnusedName();
1181
+ return new Promise((resolve, reject) => {
1182
+ registrationSignal?.addEventListener("abort", () => {
1183
+ this.#tools.delete(name);
1184
+ frames.notify(exposedOrigins);
1185
+ reject(registrationSignal.reason);
1186
+ }, { once: true });
1187
+ this.#tools.set(name, storedTool);
1188
+ frames.notify(exposedOrigins).then(resolve, reject);
1189
+ });
1190
+ }
1191
+ async getTools(options = {}) {
1192
+ const ownerDocument = this.#document;
1193
+ const fromOrigins = readOriginSequence(readDictionary(options).fromOrigins);
1194
+ requireActiveWindow(ownerDocument);
1195
+ const frames = await this.#requireFrames();
1196
+ const requestedOrigins = parseOrigins(fromOrigins);
1197
+ const tools = await frames.getTools(requestedOrigins);
1198
+ tools.sort((left, right) => {
1199
+ if (left.name === right.name) return 0;
1200
+ return left.name < right.name ? -1 : 1;
1201
+ });
1202
+ return new Promise((resolve) => {
1203
+ queueTask(() => resolve(tools));
1204
+ });
1205
+ }
1206
+ async executeTool(tool, inputObject = {}, options = {}) {
1207
+ const ownerDocument = this.#document;
1208
+ const target = readExecutionTarget(tool);
1209
+ const callerSignal = readAbortSignal(readDictionary(options).signal);
1210
+ requireActiveWindow(ownerDocument);
1211
+ const expectedOrigin = URL.parse(target.origin)?.origin;
1212
+ if (!expectedOrigin || expectedOrigin === "null") throw new NativeDOMException("Invalid or opaque origin", "NotSupportedError");
1213
+ if (!isObject(inputObject)) throw new TypeError("inputObject must be an object");
1214
+ const serializedInput = serializeJSON(inputObject);
1215
+ callerSignal?.throwIfAborted();
1216
+ const frames = await this.#requireFrames();
1217
+ callerSignal?.throwIfAborted();
1218
+ const callerWindow = requireActiveWindow(ownerDocument);
1219
+ if (target.window !== callerWindow) return frames.execute(target.window, expectedOrigin, target.name, serializedInput, callerSignal);
1220
+ return this.#executeLocal(target.name, serializedInput, expectedOrigin, callerWindow.origin, callerSignal);
1221
+ }
1222
+ #executeLocal(name, serializedInput, expectedOrigin, callerOrigin, callerSignal) {
1223
+ return new Promise((resolve, reject) => {
1224
+ callerSignal?.throwIfAborted();
1225
+ const callbackController = new AbortController();
1226
+ let callbackFinished = false;
1227
+ const onCallerAbort = () => {
1228
+ reject(callerSignal.reason);
1229
+ queueTask(() => {
1230
+ if (!callbackFinished) callbackController.abort();
1231
+ });
1232
+ };
1233
+ const rejectExecution = () => {
1234
+ callbackFinished = true;
1235
+ queueTask(() => {
1236
+ callerSignal?.removeEventListener("abort", onCallerAbort);
1237
+ reject(executionError());
1238
+ });
1239
+ };
1240
+ const completeExecution = (value) => {
1241
+ callbackFinished = true;
1242
+ if (callerSignal?.aborted) return;
1243
+ try {
1244
+ const serializedResult = serializeJSON(value);
1245
+ queueTask(() => {
1246
+ callerSignal?.removeEventListener("abort", onCallerAbort);
1247
+ resolve(serializedResult);
1248
+ });
1249
+ } catch {
1250
+ rejectExecution();
1251
+ }
1252
+ };
1253
+ const dispatchTool = () => {
1254
+ if (callerSignal?.aborted) return;
1255
+ try {
1256
+ const ownerWindow = requireActiveWindow(this.#document);
1257
+ const storedTool = this.#tools.get(name);
1258
+ if (!storedTool || expectedOrigin !== ownerWindow.origin || !isExposedTo(storedTool, ownerWindow.origin, callerOrigin)) {
1259
+ rejectExecution();
1260
+ return;
1261
+ }
1262
+ const input = JSON.parse(serializedInput);
1263
+ if (!isObject(input)) {
1264
+ rejectExecution();
1265
+ return;
1266
+ }
1267
+ const execute = storedTool.execute;
1268
+ const callbackResult = execute(input, { signal: callbackController.signal });
1269
+ Promise.resolve(callbackResult).then(completeExecution, rejectExecution);
1270
+ } catch {
1271
+ rejectExecution();
1272
+ }
1273
+ };
1274
+ callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
1275
+ queueTask(dispatchTool);
1276
+ });
1277
+ }
1278
+ async #requireFrames() {
1279
+ const frames = this.#frames;
1280
+ if (!frames || !await frames.allowed()) throw new NativeDOMException("WebMCP is disabled by Permissions Policy", "NotAllowedError");
1281
+ requireActiveWindow(this.#document);
1282
+ return frames;
1283
+ }
1284
+ #exposedTools(callerOrigin) {
1285
+ const ownerOrigin = requireActiveWindow(this.#document).origin;
1286
+ const exposed = [];
1287
+ for (const storedTool of this.#tools.values()) {
1288
+ if (!isExposedTo(storedTool, ownerOrigin, callerOrigin)) continue;
1289
+ exposed.push(storedTool.metadata);
1290
+ }
1291
+ return exposed;
1292
+ }
1293
+ #queueToolChange() {
1294
+ return new Promise((resolve) => {
1295
+ queueTask(() => {
1296
+ this.dispatchEvent(new Event("toolchange"));
1297
+ resolve();
1298
+ });
1299
+ });
1300
+ }
1301
+ };
1302
+ function isExposedTo(tool, ownerOrigin, callerOrigin) {
1303
+ return ownerOrigin === callerOrigin || tool.exposedTo.includes(callerOrigin);
1304
+ }
1305
+ const modelContextConstructor = function ModelContext() {
1306
+ throw new TypeError("Illegal constructor");
1307
+ };
1308
+ Object.defineProperty(modelContextConstructor, "name", { value: "ModelContext" });
1309
+ Object.defineProperty(modelContextConstructor, "prototype", {
1310
+ value: ModelContextPolyfill.prototype,
1311
+ writable: false
1312
+ });
1313
+ Object.setPrototypeOf(modelContextConstructor, EventTarget);
1314
+ Object.defineProperties(ModelContextPolyfill.prototype, {
1315
+ constructor: {
1316
+ value: modelContextConstructor,
1317
+ configurable: true,
1318
+ writable: true
1319
+ },
1320
+ [Symbol.toStringTag]: {
1321
+ value: "ModelContext",
1322
+ configurable: true
1323
+ },
1324
+ registerTool: { enumerable: true },
1325
+ getTools: { enumerable: true },
1326
+ executeTool: { enumerable: true },
1327
+ ontoolchange: { enumerable: true }
1328
+ });
1329
+ function readToolDefinition(value) {
1330
+ const descriptor = readDictionary(value);
1331
+ const annotations = readAnnotations(descriptor.annotations);
1332
+ const description = toDOMString(requireMember(descriptor.description, "description"));
1333
+ const callback = requireMember(descriptor.execute, "execute");
1334
+ if (typeof callback !== "function") throw new TypeError("execute must be a function");
1335
+ const execute = callback;
1336
+ const inputSchema = readInputSchema(descriptor.inputSchema);
1337
+ const name = toDOMString(requireMember(descriptor.name, "name"));
1338
+ const rawTitle = descriptor.title;
1339
+ return {
1340
+ name,
1341
+ title: rawTitle === void 0 ? "" : toUSVString(rawTitle),
1342
+ description,
1343
+ annotations,
1344
+ inputSchema,
1345
+ execute
1346
+ };
1347
+ }
1348
+ function readExecutionTarget(value) {
1349
+ const descriptor = readDictionary(value);
1350
+ readAnnotations(descriptor.annotations);
1351
+ toDOMString(requireMember(descriptor.description, "description"));
1352
+ readInputSchema(descriptor.inputSchema);
1353
+ const name = toDOMString(requireMember(descriptor.name, "name"));
1354
+ const origin = toUSVString(requireMember(descriptor.origin, "origin"));
1355
+ const title = descriptor.title;
1356
+ if (title !== void 0) toDOMString(title);
1357
+ const targetWindow = requireMember(descriptor.window, "window");
1358
+ if (!isObject(targetWindow)) throw new TypeError("window must be a Window");
1359
+ getWindow.call(targetWindow);
1360
+ return {
1361
+ name,
1362
+ origin,
1363
+ window: targetWindow
1364
+ };
1365
+ }
1366
+ function readInputSchema(value) {
1367
+ if (value !== void 0 && !isObject(value)) throw new TypeError("inputSchema must be an object");
1368
+ return value;
1369
+ }
1370
+ function isObject(value) {
1371
+ return typeof value === "object" && value !== null || typeof value === "function";
1372
+ }
1373
+ function readDictionary(value) {
1374
+ if (value == null) return {};
1375
+ if (!isObject(value)) throw new TypeError("Expected a dictionary");
1376
+ return value;
1377
+ }
1378
+ function readAnnotations(value) {
1379
+ if (value === void 0) return;
1380
+ const dictionary = readDictionary(value);
1381
+ const annotations = {};
1382
+ for (const name of annotationNames) annotations[name] = Boolean(dictionary[name]);
1383
+ return annotations;
1384
+ }
1385
+ function toDOMString(value) {
1386
+ if (typeof value === "symbol") throw new TypeError("Cannot convert a Symbol to a string");
1387
+ return String(value);
1388
+ }
1389
+ function toUSVString(value) {
1390
+ return toDOMString(value).toWellFormed();
1391
+ }
1392
+ function requireMember(value, name) {
1393
+ if (value === void 0) throw new TypeError(`${name} is required`);
1394
+ return value;
1395
+ }
1396
+ function serializeJSON(value) {
1397
+ const result = JSON.stringify(value);
1398
+ if (result === void 0) throw new TypeError("Value is not JSON-serializable");
1399
+ return result;
1400
+ }
1401
+ function readAbortSignal(value) {
1402
+ if (value === void 0) return;
1403
+ return AbortSignal.any([value]);
1404
+ }
1405
+ function readOriginSequence(value) {
1406
+ if (value === void 0) return [];
1407
+ if (!isObject(value)) throw new TypeError("Origins must be a sequence");
1408
+ const getIterator = readDictionary(value)[Symbol.iterator];
1409
+ if (typeof getIterator !== "function") throw new TypeError("Origins must be a sequence");
1410
+ const iterable = { [Symbol.iterator]() {
1411
+ const iterator = Reflect.apply(getIterator, value, []);
1412
+ if (!isObject(iterator)) throw new TypeError("Iterator must be an object");
1413
+ const next = readDictionary(iterator).next;
1414
+ if (typeof next !== "function") throw new TypeError("Iterator next must be a function");
1415
+ return { next() {
1416
+ return Reflect.apply(next, iterator, []);
1417
+ } };
1418
+ } };
1419
+ return Array.from(iterable, toUSVString);
1420
+ }
1421
+ function parseOrigins(origins) {
1422
+ return origins.map((value) => {
1423
+ const origin = URL.parse(value)?.origin;
1424
+ if (origin === void 0) throw new NativeDOMException("Invalid origin", "SecurityError");
1425
+ const url = URL.parse(origin);
1426
+ if (!url || !isPotentiallyTrustworthy(url)) throw new NativeDOMException("Origin is not potentially trustworthy", "SecurityError");
1427
+ return origin;
1428
+ });
1429
+ }
1430
+ function isPotentiallyTrustworthy({ protocol, hostname }) {
1431
+ if (["https:", "wss:"].includes(protocol)) return true;
1432
+ const isLoopback = hostname === "[::1]" || /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname);
1433
+ const isLocalhost = hostname === "localhost" || hostname === "localhost." || hostname.endsWith(".localhost") || hostname.endsWith(".localhost.");
1434
+ return ["http:", "ws:"].includes(protocol) && (isLoopback || isLocalhost);
1435
+ }
1436
+ function requireActiveWindow(owner) {
1437
+ const view = activeWindow(owner);
1438
+ if (!view) throw new NativeDOMException("The document is not fully active", "InvalidStateError");
1439
+ if (readToolsPolicy(owner) === false) throw new NativeDOMException("WebMCP is disabled by Permissions Policy", "NotAllowedError");
1440
+ return view;
1441
+ }
1442
+ let taskPort;
1443
+ const queuedTasks = [];
1444
+ function queueTask(callback) {
1445
+ if (!taskPort) {
1446
+ const channel = new MessageChannel();
1447
+ channel.port1.onmessage = () => queuedTasks.shift()?.();
1448
+ taskPort = channel.port2;
1449
+ }
1450
+ queuedTasks.push(callback);
1451
+ taskPort.postMessage(void 0);
1452
+ }
1453
+ /**
1454
+ * Installs upstream WebMCP and the temporary declarative tools layer.
1455
+ * Engines missing an API the vendored core calls are left untouched: upstream defines
1456
+ * the document getter before it constructs the context, so a failed construction would
1457
+ * leave `document.modelContext` throwing.
1458
+ */
1459
+ function installWebMCP() {
1460
+ if (typeof document === "undefined" || !globalThis.isSecureContext) return;
1461
+ if (typeof Promise.withResolvers !== "function" || typeof AbortSignal.any !== "function" || typeof String.prototype.toWellFormed !== "function" || typeof URL.parse !== "function") return;
1462
+ installWebMCP$1();
1463
+ const context = document.modelContext;
1464
+ if (context) installWebMCPDeclarativeExtensions(context);
1465
+ }
1466
+
1467
+ //#endregion
1468
+ //#region src/subsystems/mcp/webmcp.ts
1469
+ var webmcp_exports = /* @__PURE__ */ __exportAll({
1470
+ CONSEQUENTIAL_TOOL_NAMES: () => CONSEQUENTIAL_TOOL_NAMES,
1471
+ READ_ONLY_TOOL_NAMES: () => READ_ONLY_TOOL_NAMES,
1472
+ addWebMcpEventListener: () => addWebMcpEventListener,
1473
+ executeWebMcpTool: () => executeWebMcpTool,
1474
+ extractAbortSignal: () => extractAbortSignal,
1475
+ getWebMcpEventTarget: () => getWebMcpEventTarget,
1476
+ getWebMcpMode: () => getWebMcpMode,
1477
+ getWebMcpTools: () => getWebMcpTools,
1478
+ onWebMcpToolActivated: () => onWebMcpToolActivated,
1479
+ onWebMcpToolCancel: () => onWebMcpToolCancel,
1480
+ onWebMcpToolChange: () => onWebMcpToolChange,
1481
+ parseWebMcpInputSchema: () => parseWebMcpInputSchema,
1482
+ registerWebMcpTools: () => registerWebMcpTools,
1483
+ resolveWebMcpToolAnnotations: () => resolveWebMcpToolAnnotations,
1484
+ setWebMcpMode: () => setWebMcpMode,
1485
+ unregisterWebMcpTools: () => unregisterWebMcpTools
1486
+ });
1487
+ const registeredToolControllers = /* @__PURE__ */ new Map();
1488
+ const registeredToolNames = /* @__PURE__ */ new Set();
1489
+ let currentMode = "polyfill";
1490
+ /**
1491
+ * Set the WebMCP mode (polyfill vs native).
1492
+ *
1493
+ * - `"polyfill"` (default): Uses `@mcp-b/webmcp-polyfill` for a pure-JS
1494
+ * `document.modelContext` that works in all browsers.
1495
+ * - `"native"`: Uses Chrome's native `document.modelContext` from
1496
+ * `chrome://flags/#enable-webmcp-testing`. Requires the flag to be
1497
+ * enabled. May crash in early Canary builds.
1498
+ *
1499
+ * Detection follows Chrome's recommended compat pattern:
1500
+ * `document.modelContext || navigator.modelContext`
1501
+ * `navigator.modelContext` was deprecated in Chrome 150 and removed in
1502
+ * Chrome 152.0.7943.0, but the fallback keeps older clients working.
1503
+ */
1504
+ function setWebMcpMode(mode) {
1505
+ currentMode = mode;
1506
+ }
1507
+ function getWebMcpMode() {
1508
+ return currentMode;
1509
+ }
1510
+ /**
1511
+ * Install the `@mcp-b/webmcp-polyfill` onto `document.modelContext`.
1512
+ *
1513
+ * In `@mcp-b/webmcp-polyfill` 6.0.0+, the entry point is `installWebMCP()`
1514
+ * (previously `initializeWebMCPPolyfill()` in v5).
1515
+ * The polyfill itself bails out when Chrome's native `document.modelContext`
1516
+ * is already present — it does NOT override the native API. We call it
1517
+ * unconditionally; if the polyfill is already installed it returns quickly.
1518
+ */
1519
+ function ensurePolyfill() {
1520
+ try {
1521
+ if (typeof globalThis !== "undefined" && typeof globalThis.isSecureContext === "undefined" && typeof location !== "undefined" && (location.protocol === "https:" || location.hostname === "localhost" || location.hostname === "127.0.0.1")) try {
1522
+ Object.defineProperty(globalThis, "isSecureContext", {
1523
+ value: true,
1524
+ configurable: true
1525
+ });
1526
+ } catch {}
1527
+ if (typeof installWebMCP === "function") installWebMCP();
1528
+ } catch (err) {
1529
+ console.warn("WebMCP polyfill initialization failed:", err);
1530
+ }
1531
+ }
1532
+ /**
1533
+ * Returns true when the `@mcp-b/webmcp-polyfill` v5.1.0+ security gate will
1534
+ * allow `registerTool()` to proceed.
1535
+ *
1536
+ * The polyfill's `validateOriginAgentCluster()` throws `DOMException("",
1537
+ * "SecurityError")` when `globalThis.originAgentCluster === false` (and the
1538
+ * protocol is not `file:`). Chrome automatically origin-isolates HTTPS pages so
1539
+ * this is never an issue there. Firefox ≥ 138 exposes `originAgentCluster` but
1540
+ * returns `false` unless the server sends `Origin-Agent-Cluster: ?1` — a
1541
+ * header that GitHub Pages does not set. This check lets us detect Firefox
1542
+ * (and any other browser with the same behaviour) early and skip registration
1543
+ * with a single descriptive warning instead of N per-tool `DOMException`s.
1544
+ *
1545
+ * Native mode bypasses the polyfill entirely and is unaffected.
1546
+ */
1547
+ function isOriginAgentClusterCompatible() {
1548
+ if (typeof globalThis.originAgentCluster === "undefined") return true;
1549
+ if (globalThis.location?.protocol === "file:") return true;
1550
+ return globalThis.originAgentCluster !== false;
1551
+ }
1552
+ /**
1553
+ * Access the WebMCP ModelContext API.
1554
+ *
1555
+ * In polyfill mode: installs the `@mcp-b/webmcp-polyfill` which surfaces
1556
+ * `document.modelContext`.
1557
+ * In native mode: follows Chrome's recommended feature-detection pattern:
1558
+ * `document.modelContext || navigator.modelContext`
1559
+ * `navigator.modelContext` was deprecated in Chrome 150 and removed in
1560
+ * Chrome 152.0.7943.0, but the fallback preserves compatibility with
1561
+ * earlier builds that may still be in the wild.
1562
+ */
1563
+ function getModelContextApi() {
1564
+ if (typeof document === "undefined" && typeof navigator === "undefined") return null;
1565
+ if (currentMode === "polyfill") ensurePolyfill();
1566
+ try {
1567
+ const modelContext = typeof document !== "undefined" && typeof document.modelContext !== "undefined" ? Reflect.get(document, "modelContext") : typeof navigator !== "undefined" && typeof navigator.modelContext !== "undefined" ? Reflect.get(navigator, "modelContext") : void 0;
1568
+ if (!modelContext || typeof modelContext !== "object") return null;
1569
+ const api = modelContext;
1570
+ if (typeof api.registerTool !== "function") return null;
1571
+ return api;
1572
+ } catch (err) {
1573
+ console.warn("WebMCP modelContext access failed:", err);
1574
+ return null;
1575
+ }
1576
+ }
1577
+ /**
1578
+ * Safely parse or normalize WebMCP input schema.
1579
+ *
1580
+ * Starting in Chrome 154.0.8014.0 (PR #241), `RegisteredTool#inputSchema` returns
1581
+ * a JavaScript object (a deep copy of the schema provided at registration).
1582
+ * In Chrome < 154.0.8014.0, `RegisteredTool#inputSchema` returned a stringified JSON schema (DOMString).
1583
+ *
1584
+ * This function provides backwards-compatible schema parsing:
1585
+ * - If inputSchema is a string: attempts JSON.parse.
1586
+ * - If inputSchema is an object: returns the object.
1587
+ * - Fallbacks gracefully to an empty object schema if null, undefined, or invalid JSON.
1588
+ */
1589
+ function parseWebMcpInputSchema(inputSchema) {
1590
+ if (typeof inputSchema === "string") {
1591
+ try {
1592
+ const parsed = JSON.parse(inputSchema);
1593
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
1594
+ } catch {}
1595
+ return {
1596
+ type: "object",
1597
+ properties: {}
1598
+ };
1599
+ }
1600
+ if (inputSchema && typeof inputSchema === "object" && !Array.isArray(inputSchema)) return inputSchema;
1601
+ return {
1602
+ type: "object",
1603
+ properties: {}
1604
+ };
1605
+ }
1606
+ /**
1607
+ * Safely extract AbortSignal from execute options/context.
1608
+ * Supports Chrome 153+ options object `{ signal }` (PR #247), direct AbortSignal instances,
1609
+ * or undefined for backward compatibility with Chrome < 153 and legacy callers.
1610
+ */
1611
+ function extractAbortSignal(context) {
1612
+ if (!context) return;
1613
+ if (typeof AbortSignal !== "undefined" && context instanceof AbortSignal) return context;
1614
+ if (typeof context === "object" && "signal" in context && typeof AbortSignal !== "undefined" && context.signal instanceof AbortSignal) return context.signal;
1615
+ }
1616
+ /**
1617
+ * Retrieve tools registered on the WebMCP ModelContext API.
1618
+ *
1619
+ * Practicing graceful degradation:
1620
+ * - Feature-detects `getTools()` on `document.modelContext` / `navigator.modelContext`.
1621
+ * - If `getTools()` is absent (older browser builds or polyfills without getTools),
1622
+ * returns an empty array `[]` without throwing.
1623
+ * - Supports options dictionary with `fromOrigins` to discover cross-frame/cross-origin tools (Chrome 155+).
1624
+ * - Normalizes `RegisteredTool#inputSchema` so caller receives a JavaScript object
1625
+ * whether running on Chrome 154+ (native object) or Chrome < 154 (DOMString JSON).
1626
+ */
1627
+ async function getWebMcpTools(options) {
1628
+ const modelContext = getModelContextApi();
1629
+ if (!modelContext || typeof modelContext.getTools !== "function") return [];
1630
+ try {
1631
+ const rawTools = await modelContext.getTools(options);
1632
+ if (!Array.isArray(rawTools)) return [];
1633
+ return rawTools.map((tool) => ({
1634
+ name: tool.name,
1635
+ ...tool.title ? { title: tool.title } : {},
1636
+ ...tool.description ? { description: tool.description } : {},
1637
+ inputSchema: parseWebMcpInputSchema(tool.inputSchema),
1638
+ ...tool.annotations ? { annotations: tool.annotations } : {},
1639
+ ...tool.execute ? { execute: tool.execute } : {}
1640
+ }));
1641
+ } catch (err) {
1642
+ console.warn("Failed to retrieve WebMCP tools via getTools():", err);
1643
+ return [];
1644
+ }
1645
+ }
1646
+ /**
1647
+ * Gracefully execute a WebMCP tool on a ModelContext instance with backwards compatibility.
1648
+ *
1649
+ * Starting in Chrome 155.0.8052.0 (PR #246, PR #251), `document.modelContext.executeTool()`
1650
+ * accepts an optional JavaScript object directly instead of a JSON DOMString.
1651
+ * In Chrome < 155 and earlier polyfills, `executeTool()` required a stringified JSON arguments string.
1652
+ *
1653
+ * This function practices graceful degradation:
1654
+ * 1. Prepares both an `inputObject` (for Chrome 155+) and an `inputStr` (for Chrome < 155 & polyfills).
1655
+ * 2. Attempts to execute `executeTool(tool, inputObject, options)` first.
1656
+ * 3. Catches errors starting with "Failed to parse input" (or TypeErrors from WebIDL DOMString checks)
1657
+ * and retries transparently with `JSON.stringify(inputObject)`.
1658
+ * 4. Re-throws any unrelated execution errors.
1659
+ */
1660
+ async function executeWebMcpTool(modelContext, tool, input, options) {
1661
+ if (!modelContext || typeof modelContext.executeTool !== "function") throw new Error("ModelContext executeTool is not available");
1662
+ let inputObject;
1663
+ let inputStr;
1664
+ if (typeof input === "string") {
1665
+ inputStr = input;
1666
+ try {
1667
+ const parsed = JSON.parse(input);
1668
+ inputObject = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1669
+ } catch {
1670
+ inputObject = {};
1671
+ }
1672
+ } else if (input && typeof input === "object" && !Array.isArray(input)) {
1673
+ inputObject = input;
1674
+ try {
1675
+ inputStr = JSON.stringify(input);
1676
+ } catch {
1677
+ inputStr = "{}";
1678
+ }
1679
+ } else {
1680
+ inputObject = {};
1681
+ inputStr = "{}";
1682
+ }
1683
+ try {
1684
+ return await modelContext.executeTool(tool, inputObject, options);
1685
+ } catch (err) {
1686
+ const isParseError = typeof err?.message === "string" && err.message.startsWith("Failed to parse input");
1687
+ const isTypeError = err instanceof TypeError;
1688
+ if (isParseError || isTypeError) return await modelContext.executeTool(tool, inputStr, options);
1689
+ throw err;
1690
+ }
1691
+ }
1692
+ /**
1693
+ * Resolves the appropriate EventTarget for WebMCP lifecycle events
1694
+ * (`toolactivated` and `toolcancel`) with backwards compatibility.
1695
+ *
1696
+ * Starting in Chrome 156.0.8076.0 (PR #245), `ontoolactivated` and `ontoolcancel`
1697
+ * event handler properties and lifecycle events moved from `window` to `document.modelContext`.
1698
+ * In Chrome < 156, these events were fired on `window`.
1699
+ *
1700
+ * This function practices robust feature detection and graceful degradation:
1701
+ * 1. Safely checks for existence of `document` and `window` to avoid ReferenceErrors
1702
+ * in non-DOM / headless / Node.js environments.
1703
+ * 2. Checks if `on${type}` is present in `document.modelContext` (or `navigator.modelContext`).
1704
+ * If present and it supports `addEventListener`, `modelContext` is returned.
1705
+ * 3. Checks if `on${type}` is present in `window`. If present and `window` supports
1706
+ * `addEventListener`, `window` is returned (legacy Chrome < 156 path).
1707
+ * 4. Fallback: If neither has `on${type}` (e.g. polyfill, synthetic mock, or uninitialized prototype),
1708
+ * prefers `modelContext` if it supports `addEventListener`, otherwise falls back to `window`.
1709
+ * 5. Returns `null` if no valid EventTarget is found.
1710
+ */
1711
+ function getWebMcpEventTarget(type) {
1712
+ const doc = typeof document !== "undefined" ? document : void 0;
1713
+ const win = typeof window !== "undefined" ? window : void 0;
1714
+ const nav = typeof navigator !== "undefined" ? navigator : void 0;
1715
+ const modelContext = doc?.modelContext ?? nav?.modelContext;
1716
+ const hasModelContextListener = modelContext && typeof modelContext.addEventListener === "function";
1717
+ const hasWindowListener = win && typeof win.addEventListener === "function";
1718
+ if (type) {
1719
+ const handlerKey = `on${type}`;
1720
+ if (modelContext && handlerKey in modelContext && hasModelContextListener) return modelContext;
1721
+ if (win && handlerKey in win && hasWindowListener) return win;
1722
+ }
1723
+ if (hasModelContextListener) return modelContext;
1724
+ if (hasWindowListener) return win;
1725
+ return null;
1726
+ }
1727
+ /**
1728
+ * Safely registers a listener for WebMCP lifecycle events (`toolactivated`, `toolcancel`)
1729
+ * with graceful degradation, event payload normalization, and automatic unsubscription.
1730
+ *
1731
+ * Returns an idempotent unsubscribe cleanup function.
1732
+ */
1733
+ function addWebMcpEventListener(type, listener, options) {
1734
+ const target = getWebMcpEventTarget(type);
1735
+ const opts = typeof options === "object" ? options : void 0;
1736
+ const listenBoth = opts?.listenBoth ?? false;
1737
+ const win = typeof window !== "undefined" && typeof window.addEventListener === "function" ? window : null;
1738
+ const doc = typeof document !== "undefined" ? document : null;
1739
+ const nav = typeof navigator !== "undefined" ? navigator : null;
1740
+ const modelContext = doc?.modelContext ?? nav?.modelContext;
1741
+ const hasMc = modelContext && typeof modelContext.addEventListener === "function";
1742
+ const secondaryTarget = listenBoth ? target === modelContext ? win !== target ? win : null : hasMc ? modelContext : null : null;
1743
+ if (!target && !secondaryTarget) return () => {};
1744
+ let recentToolNames = null;
1745
+ let clearTimer = null;
1746
+ const wrapHandler = (evt) => {
1747
+ let toolName = evt.toolName;
1748
+ if (typeof toolName !== "string" && "detail" in evt && typeof evt.detail === "object") toolName = evt.detail?.toolName;
1749
+ if (typeof toolName !== "string") toolName = "";
1750
+ if (!("toolName" in evt) || evt.toolName !== toolName) try {
1751
+ Object.defineProperty(evt, "toolName", {
1752
+ value: toolName,
1753
+ configurable: true,
1754
+ enumerable: true
1755
+ });
1756
+ } catch {}
1757
+ if (listenBoth && toolName) {
1758
+ if (!recentToolNames) recentToolNames = /* @__PURE__ */ new Set();
1759
+ if (recentToolNames.has(toolName)) return;
1760
+ recentToolNames.add(toolName);
1761
+ if (!clearTimer) clearTimer = setTimeout(() => {
1762
+ recentToolNames?.clear();
1763
+ clearTimer = null;
1764
+ }, 100);
1765
+ }
1766
+ listener(evt);
1767
+ };
1768
+ if (target) target.addEventListener(type, wrapHandler, options);
1769
+ if (secondaryTarget) secondaryTarget.addEventListener(type, wrapHandler, options);
1770
+ let unsubscribed = false;
1771
+ const unsubscribe = () => {
1772
+ if (unsubscribed) return;
1773
+ unsubscribed = true;
1774
+ if (clearTimer) {
1775
+ clearTimeout(clearTimer);
1776
+ clearTimer = null;
1777
+ }
1778
+ if (target) target.removeEventListener(type, wrapHandler, options);
1779
+ if (secondaryTarget) secondaryTarget.removeEventListener(type, wrapHandler, options);
1780
+ };
1781
+ if (opts?.signal) {
1782
+ if (opts.signal.aborted) {
1783
+ unsubscribe();
1784
+ return () => {};
1785
+ }
1786
+ opts.signal.addEventListener("abort", () => unsubscribe(), { once: true });
1787
+ }
1788
+ return unsubscribe;
1789
+ }
1790
+ /**
1791
+ * Convenience helper to subscribe to tool execution start events (`toolactivated`).
1792
+ * Returns an unsubscribe cleanup function.
1793
+ */
1794
+ function onWebMcpToolActivated(listener, options) {
1795
+ return addWebMcpEventListener("toolactivated", listener, options);
1796
+ }
1797
+ /**
1798
+ * Convenience helper to subscribe to tool execution cancel events (`toolcancel`).
1799
+ * Returns an unsubscribe cleanup function.
1800
+ */
1801
+ function onWebMcpToolCancel(listener, options) {
1802
+ return addWebMcpEventListener("toolcancel", listener, options);
1803
+ }
1804
+ /**
1805
+ * Convenience helper to subscribe to tool registration change events (`toolchange`).
1806
+ * Dispatched on `document.modelContext` when tools are registered or unregistered.
1807
+ * Returns an unsubscribe cleanup function.
1808
+ */
1809
+ function onWebMcpToolChange(listener, options) {
1810
+ const doc = typeof document !== "undefined" ? document : void 0;
1811
+ const nav = typeof navigator !== "undefined" ? navigator : void 0;
1812
+ const modelContext = doc?.modelContext ?? nav?.modelContext;
1813
+ if (!modelContext || typeof modelContext.addEventListener !== "function") return () => {};
1814
+ modelContext.addEventListener("toolchange", listener, options);
1815
+ return () => {
1816
+ try {
1817
+ modelContext.removeEventListener("toolchange", listener, options);
1818
+ } catch {}
1819
+ };
1820
+ }
1821
+ /**
1822
+ * Consequential tools that perform high-stakes, irreversible, or external real-world actions
1823
+ * (such as deleting data, executing arbitrary system commands, or transmitting live communications).
1824
+ * Signals consuming agents to require explicit user confirmation prior to execution (Chrome 154.0.8017.0+).
1825
+ */
1826
+ const CONSEQUENTIAL_TOOL_NAMES = /* @__PURE__ */ new Set([
1827
+ "bash",
1828
+ "clear_chat",
1829
+ "delete_file",
1830
+ "delete_task",
1831
+ "email_send_message",
1832
+ "git_delete_branch",
1833
+ "git_delete_repo",
1834
+ "git_push",
1835
+ "git_reset"
1836
+ ]);
1837
+ /**
1838
+ * Read-only tools that query information without mutating state.
1839
+ */
1840
+ const READ_ONLY_TOOL_NAMES = /* @__PURE__ */ new Set([
1841
+ "ask_user",
1842
+ "detect_language",
1843
+ "diff_files",
1844
+ "email_read_messages",
1845
+ "fetch_url",
1846
+ "get_current_time",
1847
+ "git_branches",
1848
+ "git_diff",
1849
+ "git_list_repos",
1850
+ "git_log",
1851
+ "git_read_file_at_ref",
1852
+ "git_show",
1853
+ "git_status",
1854
+ "list_components",
1855
+ "list_files",
1856
+ "list_room_members",
1857
+ "list_tasks",
1858
+ "list_tool_profiles",
1859
+ "proofread_text",
1860
+ "read_file",
1861
+ "remote_mcp_list_tools",
1862
+ "rewrite_text",
1863
+ "search_files",
1864
+ "summarize_text",
1865
+ "translate_text",
1866
+ "web_search",
1867
+ "write_text"
1868
+ ]);
1869
+ /**
1870
+ * Resolve annotations for a WebMCP tool registration.
1871
+ * Allows custom annotations (from declarative or customized tool definitions)
1872
+ * to override defaults.
1873
+ */
1874
+ function resolveWebMcpToolAnnotations(toolName, customAnnotations) {
1875
+ const isConsequential = CONSEQUENTIAL_TOOL_NAMES.has(toolName);
1876
+ return {
1877
+ readOnlyHint: !isConsequential && READ_ONLY_TOOL_NAMES.has(toolName),
1878
+ consequentialHint: isConsequential,
1879
+ untrustedContentHint: true,
1880
+ ...typeof customAnnotations?.debugging === "boolean" ? { debugging: customAnnotations.debugging } : {},
1881
+ ...customAnnotations || {}
1882
+ };
1883
+ }
1884
+ /**
1885
+ * Register ShadowClaw tools with the WebMCP ModelContext API.
1886
+ *
1887
+ * Both the `@mcp-b/webmcp-polyfill` and Chrome's native API support
1888
+ * `registerTool(tool, { signal })`. We always use AbortController signals so
1889
+ * that unregistration works identically regardless of which implementation is
1890
+ * actually active.
1891
+ *
1892
+ * Key correctness detail: `ensurePolyfill()` is a no-op when
1893
+ * Chrome's native `document.modelContext` already exists — it does NOT
1894
+ * override the native API. In that situation we are talking to the native
1895
+ * Chrome API even in "polyfill" mode. The native Chrome API only supports
1896
+ * signal-based unregistration, so using AbortController signals everywhere
1897
+ * prevents "Duplicate tool name" errors when tools are re-configured.
1898
+ */
1899
+ async function registerWebMcpTools(agentWorker, _emit, groupId = DEFAULT_GROUP_ID, tools) {
1900
+ const activeTools = Array.isArray(tools) ? tools : TOOL_DEFINITIONS;
1901
+ if (activeTools.length === 0) return true;
1902
+ if (currentMode === "polyfill" && !isOriginAgentClusterCompatible()) {
1903
+ console.warn("WebMCP polyfill registration skipped: this browser reports originAgentCluster = false. The @mcp-b/webmcp-polyfill v5.1.0+ requires an origin-keyed agent cluster (Chrome enforces this automatically for HTTPS; Firefox requires the server to send Origin-Agent-Cluster: ?1). WebMCP tools are unavailable in this browser session.");
1904
+ return false;
1905
+ }
1906
+ const modelContext = getModelContextApi();
1907
+ if (!modelContext) return false;
1908
+ for (const def of activeTools) {
1909
+ if (registeredToolNames.has(def.name)) continue;
1910
+ const controller = new AbortController();
1911
+ registeredToolControllers.set(def.name, controller);
1912
+ registeredToolNames.add(def.name);
1913
+ try {
1914
+ const toolDef = {
1915
+ name: def.name,
1916
+ title: def.title || def.name,
1917
+ description: def.description,
1918
+ inputSchema: parseWebMcpInputSchema(def.input_schema),
1919
+ annotations: resolveWebMcpToolAnnotations(def.name, def.annotations),
1920
+ execute: (input, context) => {
1921
+ if (!agentWorker) throw new Error("Cannot execute tool: agent worker is not ready");
1922
+ const signal = extractAbortSignal(context);
1923
+ if (signal?.aborted) return Promise.reject(/* @__PURE__ */ new Error("Tool execution aborted"));
1924
+ return new Promise((resolve, reject) => {
1925
+ const callId = Date.now().toString(36) + Math.random().toString(36).slice(2);
1926
+ let timeout = null;
1927
+ const cleanup = () => {
1928
+ if (timeout !== null) {
1929
+ clearTimeout(timeout);
1930
+ timeout = null;
1931
+ }
1932
+ agentWorker?.removeEventListener("message", handler);
1933
+ if (signal && onAbort) signal.removeEventListener("abort", onAbort);
1934
+ };
1935
+ const onAbort = () => {
1936
+ cleanup();
1937
+ reject(/* @__PURE__ */ new Error("Tool execution aborted"));
1938
+ };
1939
+ if (signal) signal.addEventListener("abort", onAbort, { once: true });
1940
+ timeout = setTimeout(() => {
1941
+ cleanup();
1942
+ reject(/* @__PURE__ */ new Error("Timeout waiting for tool execution"));
1943
+ }, 6e5);
1944
+ const handler = (event) => {
1945
+ const data = event.data;
1946
+ if (data && data.type === "execute-tool-result" && data.callId === callId) {
1947
+ cleanup();
1948
+ if (data.error) reject(new Error(data.error));
1949
+ else resolve(data.result);
1950
+ }
1951
+ };
1952
+ agentWorker.addEventListener("message", handler);
1953
+ agentWorker.postMessage({
1954
+ type: "execute-tool",
1955
+ callId,
1956
+ payload: {
1957
+ name: def.name,
1958
+ input: input || {},
1959
+ groupId
1960
+ }
1961
+ });
1962
+ });
1963
+ }
1964
+ };
1965
+ await modelContext.registerTool(toolDef, { signal: controller.signal });
1966
+ await new Promise((resolve) => setTimeout(resolve, 0));
1967
+ } catch (err) {
1968
+ registeredToolControllers.delete(def.name);
1969
+ registeredToolNames.delete(def.name);
1970
+ if (err instanceof Error && (err.message.includes("already registered") || err.message.includes("Duplicate tool name"))) continue;
1971
+ if (err instanceof DOMException && err.name === "SecurityError") {
1972
+ console.warn("WebMCP tool registration aborted: browser security policy (SecurityError) prevented registration. Remaining tools will not be registered for this session.", err);
1973
+ return false;
1974
+ }
1975
+ console.error(`Failed to register tool ${def.name}:`, err);
1976
+ throw err;
1977
+ }
1978
+ }
1979
+ return true;
1980
+ }
1981
+ /**
1982
+ * Unregister previously registered ShadowClaw WebMCP tools.
1983
+ *
1984
+ * Aborts every AbortController associated with a registered tool. This works
1985
+ * for both the polyfill (which listens for the `abort` event on the signal it
1986
+ * was given) and Chrome's native API (which also uses signal-based lifecycle).
1987
+ * The deprecated `unregisterTool(name)` is intentionally NOT called here
1988
+ * because it is absent from the native Chrome API and causes silent failures
1989
+ * that prevent proper cleanup.
1990
+ */
1991
+ function unregisterWebMcpTools() {
1992
+ for (const controller of registeredToolControllers.values()) try {
1993
+ controller.abort();
1994
+ } catch {}
1995
+ registeredToolControllers.clear();
1996
+ registeredToolNames.clear();
1997
+ }
1998
+
1999
+ //#endregion
2000
+ export { registerWebMcpTools as a, webmcp_exports as c, onWebMcpToolChange as i, onWebMcpToolActivated as n, setWebMcpMode as o, onWebMcpToolCancel as r, unregisterWebMcpTools as s, getWebMcpMode as t };
2001
+ //# sourceMappingURL=webmcp-Bf9rT7uu.js.map